0828 面向对象
将类变成一个具体对象的过程就叫类的实例化。
构造函数(__init__())在实例化时做一些类的初始化工作。
1.__init__()构造函数、类里面定义的函数详解
代码如下:
#!/usr/bin/env python #coding:utf-8 class Role: n = 123 #类变量,存在类的内存空间里,无需实例化对象即可用print(Role.n)来调用,当然,实例化后也可以调用该变量,r2 = Role('ci','tufei','ak47') ,print(r2.n) def __init__(self,name,role,weapon,blood=100,money=15000): #构造函数 #实例化时做一些类的初始化工作 self.name = name #实例变量(又称静态属性),作用域仅限实例本身使用 self.role= role self.weapon = weapon self.blood = blood self.money = money def shot(self): # 类的方法(又称动态属性) print('%s is shot' % self.name) def buy_gun(self,gun): print('%s is buyed a %s' % (self.name,gun)) r1 = Role('zhang','jingcha','b51') #实例化一个对象r1 r2 = Role('ci','tufei','ak47') r1.shot() r1.buy_gun('ak47')
r1 = ...和 r2 = ...这就是类(Role)的实例化。
1.1 构造函数__init__(self)详解
与方法调用不同,类的实例化有点区别,上面的 r1 = Role('zhang','jingcha','b51'),如果按方法调用来看,应该是r1存了一个Role()的内存地址;其实不是,r1 = Role('zhang','jingcha','b51')的真正含义是r1 = Role(r1,'zhang','jingcha','b51'),也就是说,r1把自己也传给Role了,所以def __init__(self,name,role...)这里的self指的是“r1”,还原一下就是def __init__(r1,name,role,....),self.name = name 等价于 r1.name=name,后面的以此类推。
1.2 类中其他方法的调用详解
每个实例化对象会保存一分单独的、各不相同的 def __init__(),但是类里面的其他方法,例如上面的def shot和def buy_gun都是只保存在类Role()里面的,因为这些方法不像构造函数那样(构造函数里的数据各个对象都可能不一样),它们的代码都是固定的,所以在类里面保留一份即可,实例化的对象使用到这些方法时会去类里面调用,如上面的r1.shot(),其实是Role.shot(r1),所以说类里面的所有方法都必须有一个参数“(self)”,这个self其实就是实例化对象名(r1、r2或者其他),把这个名字传给类里的方法,以让类里的方法能区分是哪个对象在调用自己,这样类里的方法只需要保存一份,各个对象就都可以调用了。
2. 类变量、实例变量
代码如下:
class Role: n = 123 #类变量 name = 'zhangshanci' def __init__(self,name): #构造函数 #实例化时做一些类的初始化工作 self.name = name #实例变量(又称静态属性),作用域仅限实例本身使用 r1 = Role('zhang') #实例化一个对象r1 print(r1.n,r1.name) 结果: 123 zhang #说明:实例化对象后,它会现在本实例内查找有没有同名变量,如果有就引用,如果没有才去看有没有对应的类变量。所以上面的name = 'zhangshanci'没有被实例r1引用。
3. 实例变量的增删改查
类变量是无法通过实例化的对象来修改的,但是对象可以新建一个与类变量同名的实例化变量,这样就覆盖了类变量了。比如类变量n =123,新建一个实例变量r1.n = 456,那么print(r1.n)就会得到456。
修改类变量需要通过类名来修改,拿上述代码来说,要修改类变量n,需要Role.n = "456",这样再print(r2.n)就会得到456。
构造函数: class Role: def __init__(self,name,role) 实例化对象:r1 = Role('张删词','警察') r1.weapon = 'ak47' #增加一个实例变量 del r1.name #删除一个实例变量 r1.name = "童童" #修改实例变量值 print(r1.name) #查询实例变量
类变量存储各个对象公用的属性或数据;实例变量存储对象自己的属性或数据。
4.类变量的优势
class Role: guoji = "china" #类变量 name = 'zhangshanci' def __init__(self,name): self.name = name 和 class Role: name = 'zhangshanci' def __init__(self,name,guoji="china"): self.name = name self.guoji = guoji #实例变量 #他俩其实功能是一样的,区别在于在于上面用的类变量,下面用的实例变量,类变量存一份就行了,但是实例变量每个实例都会存一份,所以类变量更节省内存空间。
5. 析构函数
析构函数的作用跟构造函数作用正好相反,它是在实例释放、销毁的时候执行的,通常用于做一些收尾工作,如关闭一些数据库连接、关闭打开的临时文件。
class Role: def __init__(self,name): self.name = name def __del__(self): #析构函数无需手动调用,它会自动执行 print('this is a xigou func.') r1 = Role('zhang') del r1 #手动del,销毁了一个实例,这时候就会调用析构函数;如果没有del,则程序结束或者没有引用时就会调用析构函数。 print(r1.n,r1.name)
关于python的垃圾回收:上面的del r1,其实是删除了r1这个实例的引用,并没有删除内存里的数据,内存的数据是python虚拟机的定时回收垃圾机制清除的,也就是说,只要变量没有被引用,就会被当成垃圾回收;只要有地方引用,就永远不会被回收。
6.私有属性、私有方法
class Role: def __init__(self,name,province): self.name = name self.__province = province #双_定义私有属性 def __del__(self): print('this is a xigou func.') def __private_func(self): #双_定义私有方法 print('private') r1 = Role('zhang','sjz') r1.__private_func() #会报错,私有方法无法直接调用 print(r1.__province) #会报错,私有属性无法直接调用 #私有属性和私有方法的调用、修改方式: #在类里面建一个专门操作私有属性、私有方法的方法,然后实例调用这个方法来间接的访问私有属性、方法。
这个过程就是把属性或者方法封装了。
7.继承
继承最大的好处就是节省代码,将重复代码都通过继承来实现。
下面代码简单描述了继承和子类重写父类方法:
#!/usr/bin/env python #coding:utf-8 class People: def __init__(self,name,age): self.name = name self.age = age def eat(self): print('%s is eting.' % self.name) def sleep(self): print('%s is age.' % self.age) class Man(People): def man(self): print('this is man,%s' % self.name) def sleep(self): #重写父类sleep方法 print('chong xie sleep') def eat(self): #重写父类eat方法 People.eat(self) #执行父类的eat方法;这样虽然也不覆盖了,但是父类的方法也会执行,相当于给eat方法添加新功能了。 print('chongxie eat.') class Woman(People): def woman(self): print('this is woman,%s' % self.age) 结果: m1 = Man('ci',25) m1.eat() m1.sleep() m1.man() print('---------------------------------') w1 = Woman('wwww',33) w1.eat() w1.sleep() w1.woman()
上面的两个子类都没有定义各自的构造函数,假如子类要重写父类的构造函数,怎么做呢,代码如下:
class People: def __init__(self,name,age): self.name = name self.age = age def eat(self): print('%s is eting.' % self.name) def sleep(self): print('%s is age.' % self.age) class Man(People): def __init__(self,name,age,money): #子类的构造函数里要写所有的、包括关键参数在内的父类的构造函数里的参数 People.__init__(self, name, age) #调用一次父类的构造函数,使其生效,同时父类里的方法也都能使用这些参数了。 self.money = money
上面的“People.__init__(self, name, age)”作用与“super(Man,self).__init__(name, age)”等价,后者是新式类的写法,开发环境的话后者更合适,因为后者不需要再写一遍类名了,假如父类名称变了,那所有的子类里的构造函数都需要修改父类名,而用super()的话就不用考虑父类改名的问题了,只需要把class Man(People)里的父类名改了即可。
既然super是新式用法,功能肯定更好,还有很多好处就不列举了,先用再总结吧。
8. 多继承
多继承的N个父类的构造方法的参数必须一样,拿下面的man 继承了People和Animal来说,People的构造方法里的参数必须和Animal里的构造方法的参数一致,而且man要用super用法来向父类传递参数。
class man(People,Animal):
9. 新式类和经典类
新式类 class People(object):;经典类class People:。
新式类和经典类的区别就在于多继承时调用父类构造函数的顺序上的区别。
python3里经典类和新式类的继承方式都是广度优先。
python2里经典类是按深度优先来继承的,新式类是按广度优先来继承的。
一般来说广度优先的效率更高,更容易命中。
新式类,情况一:
class A: def __init__(self): print('A') class B(A): def __init__(self): print('B') class C(A): def __init__(self): print('C') class D(B,C): def __init__(self): print('D') d1 = D() 结果: D #因为D类有自己的构造方法,所以直接调用自己的。
情况二:
class A: def __init__(self): print('A') class B(A): def __init__(self): print('B') class C(A): def __init__(self): print('C') class D(B,C): pass # def __init__(self): # print('D') d1 = D() 结果: B #D自己没有构造方法,所以调用父类B的构造方法
情况三:
class A: def __init__(self): print('A') class B(A): pass # def __init__(self): # print('B') class C(A): def __init__(self): print('C') class D(B,C): pass # def __init__(self): # print('D') d1 = D() 结果: C #D自己没有构造方法,父类B也没有构造方法,所以调用父类C的构造方法,这里就是广度优先,如果是深度优先的话,B没有构造方法,就会调用B的父类A,然后打印A;如果A也没有,最后才会调用C的构造方法;如果都没有构造方法,那就不调用了。
情况四:
class A: def __init__(self): print('A') class B(A): pass # def __init__(self): # print('B') class C(A): pass # def __init__(self): # print('C') class D(B,C): pass # def __init__(self): # print('D') d1 = D() 结果: A #D自身、B、C都没有构造方法,所以调用C的父类A的构造方法。
10. 多态
多态的特点是一种接口,多种实现,它的作用是接口的重用。
没使用多态的代码如下:
class Animal(object): def __init__(self,name): self.name = name class cat(Animal): def talk(self): print(self.name) class dog(Animal): def talk(self): print(self.name) c1 = cat('c') c1.talk() d1 = dog('d') d1.talk() 结果: c d
这时候就思考,能不能用一个方法,来把对象传给它,它来统一调用talk()方法;所以就出现的下面的多态,代码:
class Animal(object): def __init__(self,name): self.name = name @staticmethod #静态方法 def animal_talk(obj): #接收一个对象,然后调用这个对象的talk()方法 obj.talk() class cat(Animal): def talk(self): print(self.name) class dog(Animal): def talk(self): print(self.name) c1 = cat('c') d1 = dog('d') Animal.animal_talk(c1) Animal.animal_talk(d1) 结果: c d