面向对象基础之 —— 面向对象编程与面向过程编程
一、面向过程(procedure-oriented programming):
在了解面向对象编程前,我们需要先了解面向过程编程,那么什么是面向过程
之前所写的代码大多是面向过程的,即以流程为导向,先做...后做...这其中 programer 的角色像是流水线员工、士兵,按部就班执行任务。其核心是过程,过程即解决问题的步骤,其目的是将一个复杂的问题拆分为若干个小问题,按步骤一一解决(流程化)基于该思想编写程序偏流水线式,是一种机械式思维方式
简单讲就是一种思维方式,流程化解决问题
优点:复杂的问题流程化、进而简单化
缺点:扩展性差,维护性差
使用场景:主要用于底层开发,对扩展性要求较低的软件,如系统内核,脚本程序,Apache HTTP服务器
二、函数式
将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
三、面向对象(object-oriented programming):
实现功能的方式是各个对象的交互,面向对象编程本质:使用不同对象来完成程序。其核心是对象,对象是具有某些具体功能\特征的具体事物,它具有某些属性,基于该思想编写的程序类似与创世界中上帝的思维方式
简单说就是对象间交互
优点
- I.减少代码量
- II.可扩展性强
- III.各个对象之间耦合度低
- IV.可维护性高
缺点
- I.复杂度高于面向过程
- II.无法预知执行结果
使用场景:需要较高的扩展性时(需要与用户交互的软件,如QQ)
为什么使用面向对象:
当前很多程序与用户直接打交道,而用户的需求千变万化,所以对扩展性要求非常高
面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现。类和对象是面向对象编程的两个主要方面。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能,而对象是这个根据模板创建的实例,通过实例对象可以执行类中的函数。
对象:(类的实例化/实例)即具备某些特征和技能的结合体,是具体存在的某个物体。 程序中对象指方法和属性
类:抽象概念,一系列对象的结合体(站的角度不同,总结出类是截然不同的)
作用:用于标识对象与对象之间的差异,通过类就能大致了解一个对象的特征和行为
生活中是先有对象再根据对象的特征和技能 得到一个类型,程序中是先有类才能通过类来产生对象。类包括初始化方法__init__,可以理解为构造;self,理解为this等等。
在面向对象编程中,需要遵循以下规则:
1.程序中必须先定义类
在python中程序的类用class关键字定义,程序中特征用变量标识,技能用函数标识,因而类中最常见的就是变量和函数的定义
class OldboyStudent: # 类名用大驼峰体,首字母都大写 # 补充小驼峰形式:第一个单词首字母小写,第二个首字母大写 school='oldboy' # 用变量来描述特征 def choose_course(self): #定义一个函数 print('is choosing course') # print('==========>') # ==========> 强调:在类定义阶段类体代码就会立刻执行,会产生一个类的名称空间,将类体代码执行过程中产生的名字都丢进去 通过__dict__可以获取一个对象中包含的内容 print(OldboyStudent.__dict__) # 查看类的名称空间{'__module__': '__main__', 'school': 'oldboy', 'choose_course': <function OldboyStudent.choose_course at 0x00000189231B1A60>, '__dict__': <attribute '__dict__' of 'OldboyStudent' objects>, '__weakref__': <attribute '__weakref__' of 'OldboyStudent' objects>, '__doc__': None} print(OldboyStudent.__dict__['school']) # oldboy print(OldboyStudent.__dict__['choose_course']) # <function OldboyStudent.choose_course at 0x00000206BFA41A60> OldboyStudent.__dict__['choose_course'](123) # OldboyStudent.__dict__['choose_course'] 类中的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数而已,意味着需要完全遵守函数的传参原则 OldboyStudent.__dict__['choose_course']() # 报错
使用对象的属性(访问属性使用 .语法)
python提供了专门访问属性(名称空间中的名字)的语法: 名称.属性
OldboyStudent.country='China' # OldboyStudent.__dit__['country']='China' OldboyStudent.country='CHINA' # OldboyStudent.__dit__['country']='China' print(OldboyStudent.school) # oldboy print(OldboyStudent.choose_course) # <function OldboyStudent.choose_course at 0x0000020FDD0D1A60> OldboyStudent.choose_course(111) # is choosing course
类的本质就是一个名称空间/容器,从类的名称空间中可以增/删/改/查名字
OldboyStudent.school # 查 OldboyStudent.school='Oldboy' # 改 OldboyStudent.x=1 # 增 del OldboyStudent.x # 删
2.后调用类来产生对象,创建对象,类名称后加括号即可。调用类的过程,又称为类的实例化,实例化的结果称为类的对象/实例
OldboyStudent() # 类+()不是运行类体代码,调用类会得到返回值,该返回值就是类的一个具体存在的对象/实例 # 类名+([参数]):类实例化的方式,参数是__init__方法中除了第一个self参数之外的其他参数
类中的函数第一个参数必须是self。类中定义的函数叫做 “方法”
属性与方法
对象本质也就是一个名称空间,用于存放对象自己独有的名字/属性-------成员属性(实例属性) # 这个人有自己的姓名。
类中存放的是已经定义好(名称和值)的属性---------公共属性
在声明类时,会定义好成员属性(成员属性的值是个变量)
在定义对象时,为成员属性(变量)赋值(位置参数)
公有属性与成员属性 公有属性/静态属性 可以直接通过类直接访问,也可以直接通过实例进行访问; 通过类的某个实例对公有属性进行修改,实际上是为该实例添加了一个与类的公有属性名称相同的成员属性, 对真正的公有属性是没有影响的,因此它不会影响其他实例获取的该公有属性的值; 通过类对公有属性进行修改,必然是会改变公有属性原有的值,对该类所有的实例是都有影响的。 成员属性可以直接通过实例对象来访问和更改;不能通过类来访问和修改 成员属性是每个实例对象独有的,更改实例A的属性不会影响其他实例对象的相同属性的值;
当每个对象的某些特征都相同时则放入类,当每个对象的某些特征都不相同时则放入对象
class OldboyStudent: school='oldboy' def choose_course(self): print('is choosing course') stu1=OldboyStudent() def init(obj,x,y,z): obj.name=x obj.age=y obj.sex=z # stu1.name='张三' # stu1.age=12 # stu1.sex='male' init(stu1,'张三',13,'male') print(stu1.__dict__) #{'name': '张三', 'age': 12, 'sex': 'male'}
在实例化过程中为对象定制自己独有的特征 __init__方法(初始化函数)
注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,会在创建对象时,自动执行,并传入调用类时传递的参数,可以有任意代码,但一定不能有返回值
class OldboyStudent: school='oldboy' count=0 def __init__(self,x,y,z): # self不需要手动传递,是一个形参,代表对象自己。可改,不建议 self.name=x self.age=y self.sex=z OldboyStudent.count+=1 # 类的属性发生改变,所有的属性都会发生改变 def choose_course(self): print('is choosing course') stu1=OldboyStudent('赵大',18,'male') stu2=OldboyStudent('王二',17,'male') stu3=OldboyStudent('张三',16,'male') print(OldboyStudent.count) print(stu1.count) print(stu2.count) print(stu3.count)
调用类时发生两件事
- 1.创造一个空对象
- 2.自动触发类中__init__功能执行,将stu1以及调用类括号内的参数一同写入
init函数用于初始化对象,它会在创建对象时自动执行,并传入调用类时传递的参数,第一个参数表示要初始化的对象本身
属性查找
类有两种属性:数据属性和函数属性
- 1. 类的数据属性是所有对象共享的(id都一样)
- 2. 类的函数属性是绑定给对象用的(obj.method称为绑定方法,内存地址都不一样)
id是python的实现机制,并不能真实反映内存地址,如果有内存地址,还是以内存地址为准
对象名称空间中定义的只有数据属性,而且是对象所独有的数据属性
查找顺序 —— 对象==》类。在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常
class OldboyStudent: school='oldboy' count=0 def __init__(self,x,y,z): self.name=x self.age=y self.sex=z OldboyStudent.count+=1 # 类的属性发生改变,所有的属性都会发生改变 def choose_course(self,x): print('is choosing course',self) stu1=OldboyStudent('赵大',18,'male') stu2=OldboyStudent('王二',17,'male') stu3=OldboyStudent('张三',16,'male') print(stu1.name) print(stu1.school) print(stu1.age) 类中定义的函数是类的函数属性,类可以使用,但使用的就是一个普通的函数,意味着需要完全遵循函数的参数规则 类中定义的 函数是共享个所有对象的,对象也可以使用 print(stu1.choose_course) stu1.choose_course() print(stu1)
对象的创建和初始化步骤时分开的,通常对象一旦创建 就应该进行初始化,所以最好将创建于初始化进行绑定
绑定方法(默认为绑定给对象):
在类中声明,没有加装饰器的函数,类下的对象可以调用。
特点:绑定给谁就应由谁调用,谁来调用就会将谁当作第一个参数(self)自动传入
绑定到对象的方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但是约定俗成地写出self。
class OldboyStudent: school='oldboy' count=0 def __init__(self,x,y,z): self.name=x self.age=y self.sex=z def choose_course(self,x): print('%s is choosing course'%self.name) stu1=OldboyStudent('赵大',18,'male') # stu2=OldboyStudent('王二',17,'male') # stu3=OldboyStudent('张三',16,'male') stu1.choose_course(1) # 类中定义的函数(没有被任何装饰器装饰的),其实主要是给对象使用的,而且是绑定到对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法 # 绑定方法分为两种 一种是绑定给对象的,一种绑定给类的。当要处理的数据包含在类中时,就应该绑定给类。当要处理的数据包含在对象中时,就应该绑定给对象。 # 绑定给类的方法 使用一个装饰器叫classmethod,必须有一个参数,表示当前类,参数名可自定义,不建议改 # 这是绑定给类的方法 @classmethod def print_school(cls): # 输出类里面叫school的属性 print(cls.school) # 这是绑定给对象的方法 def sayHello(self): print(self.name, " 说: 你好") 类的绑定方法,对象和类都能调用,并且能够自动传入这个类 Student.print_school() #类的调用 stu1 = Student("印度阿三","woman",20) #对象的调用 stu1.print_school()
了解:
在python3中统一了类与类型,类即类型,2没有 #类型dict就是类dict >>> list <class 'list'> class Foo: pass obj=Foo() print(type(obj)) # <class '__main__.Foo'> l=[1,2,3] print(type(l)) # <class 'list'> l.append(4) # 绑定~~~
非绑定方法
即不绑定给类也不绑定给对象。
特点:没有自动传入参数的效果,类和对象都能调用,就是一个普通函数。不需要访问类的数据,也不需要访问对象的数据,就可以作为一个非绑定方法。
class Teacher: def __init__(self,name,sex): self.name = name self.sex = sex # @staticmethod 用于定义个非绑定方法 @staticmethod def test_func(num): print("test_func run!") print(num) Teacher.test_func(1) t1 = Teacher("张三","男") t1.test_func(100) print(t1.test_func)
对象之间交互
设计王者荣耀中的英雄类,每个英雄对象可以对其他英雄对象使用技能
具备以下属性:英雄名称,等级,血量和Q_hurt,W_hurt,E_hurt 三个属性,表示各技能的伤害量
具备以下技能:Q W E
三个技能都需要一个敌方英雄作为参数,当敌方血量小于等于0时输出角色死亡
class Hero(object): def __init__(self, name, level, hp, Q_hurt, W_hurt, E_hurt): self.name = name self.level = level self.hp = hp self.Q_hurt = Q_hurt self.W_hurt = W_hurt self.E_hurt = E_hurt def use_Q(self, use1): if use1.hp <= 0: print("%s挂了" % use1.name) else: use1.hp -= self.Q_hurt print("%s HP-%s" % (use1.name, self.Q_hurt)) def use_W(self, use1): if use1.hp <= 0: print("%s挂了" % use1.name) del use1 else: use1.hp -= self.W_hurt print("%s HP-%s" % (use1.name, self.W_hurt)) def use_E(self, use1): if use1.hp <= 0: print("%s挂了" % use1.name) del use2 else: use1.hp -= self.E_hurt print("%s HP-%s" % (use1.name, self.E_hurt)) aich = Hero("aich", 10, 1000, 500, 600, 750) galen = Hero("galen", 11, 2000, 400, 500, 600) raven = Hero("raven", 11, 2000, 400, 550, 660) aich.use_Q(galen) aich.use_Q(galen) aich.use_Q(galen) print(galen.hp) raven.use_E(aich)
class Garen: #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄; camp='Demacia' #所有玩家的英雄(盖伦)的阵营都是Demacia; def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...; self.nickname=nickname #为自己的盖伦起个别名; self.aggressivity=aggressivity #英雄都有自己的攻击力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。 class Riven: camp='Noxus' #所有玩家的英雄(锐雯)的阵营都是Noxus; def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54; self.nickname=nickname #为自己的锐雯起个别名; self.aggressivity=aggressivity #英雄都有自己的攻击力; self.life_value=life_value #英雄都有自己的生命值; def attack(self,enemy): #普通攻击技能,enemy是敌人; enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。 >>> g1=Garen('草丛伦') >>> r1=Riven('锐雯雯') # garen_hero.Q()称为向garen_hero这个对象发送了一条消息,让他去执行Q这个功能,类似的有:garen_hero.W() garen_hero.E() garen_hero.R()