python之面向对象
一、对象的特性
对象的特性:
1、实例属性
定义,分两种情况:
- 类内定义:必须在__init__()方法中创建并初始化
- 类外定义:self.<属性名> = 值,可在外部创建并初始化,不建议这样做。
使用方法:
- 类内使用 self.<属性名>
- 类外使用 实例名.<属性名>
特性:
- 类被实例化以后才会有的属性
- 相同类的不同实例属性不相干
2、类属性
定义:在类内定义,无需实例化即可使用
作用:类属性使得相同类的不同实例共同持有相同变量
调用方法:
- 类名调用方式:类名.类属性
- 实例调用方法:实例名.类属性
3、私有属性
__ 开始的变量是私有成员(实例属性),意思是只有类对象自己能访问,实例对象、子类对象不能访问到这个数据。
#encoding=utf-8 class A(): def __init__(self): self.__a = 10 def info(self): print self.__a class B(A): pass if __name__ == '__main__': a = A() # print a.__a #实例对象不能访问这个数据,dir(a)中表明变量名已变为_A__a a.info() #打印信息:10 说明:类对象自己可以访问 print dir(a) #打印信息:['_A__a', '__doc__', '__init__', '__module__', 'info'] a.__a = 100 a.info() #打印信息:10 print a.__a #打印信息:100 print dir(a) #打印信息:['_A__a', '__a', '__doc__', '__init__', '__module__', 'info'] b = B() b.info() #打印信息:10 说明:子类继承父类所有的公有实例变量和方法,只有通过方法才可调用其私有实例变量 print dir(b) #打印信息:['_A__a', '__doc__', '__init__', '__module__', 'info'] b.__a = 200 b.info() #打印信息:10 print b.__a #打印信息:200 print dir(b) #打印信息:['_A__a', '__a', '__doc__', '__init__', '__module__', 'info']
说明:
Python把以两个或以上下划线字符开头且没有以两个或以上下划线结尾的变量当作私有变量,因为私有变量会在代码生成之前被转换为长格式(变为公有)。转换机制是这样的:在变量前端插入类名,再在前端加入一个下划线字符。这就是所谓的私有变量轧压(Private name mangling)
也就是self.__a = 0被转换为self._A__a = 0
如果运行代码改为这样:
if __name__ == '__main__': a = A() a.info() a._A__a = 100 a.info()
运行结果:
0 100
但不建议这样做,既已声明为私有变量,就不要在类外访问。
__ 开始的变量是私有成员(类属性),同样只有类对象自己能访问,实例对象、子类对象不能访问到这个数据;强制访问也不行。
class A(): __tmp = 'gjp' def __init__(self): self.__a = 0 def info(self): print(self.__a) print(A.__tmp) if __name__ == '__main__': print(A.__tmp) print(_A__A.__tmp)
运行报错!!!
_ 开始的变量成员变量叫做保护变量,意思是只有类对象和子类对象自己能访问到这些变量,但还是通过类提供的接口进行访问;
作为全局变量时,不能用'from module import *'导入
class A(): def __init__(self): self._a = 0 def info(self): print(self._a) class B(A): pass if __name__ == '__main__': a = A() a.info() #result:0 说明:实例a具有实例变量 + 方法,方法调用其自身实例变量,肯定是OK的 a._a = 100 a.info() #result:100 说明:a的实例变量已被重新赋值 print(a._a) #result:100 说明:a的实例变量可直接调用 b = B() b.info() #result:0 说明:子类B继承A的实例变量 + 方法 b._a = 200b b.info() #result:200 说明:b的实例变量已被重新赋值 print(b._a) #result: 200 说明:b的实例变量可直接调用
_ 开头变量类属性
class A(): tmp1 = 'www' _tmp2 = 'hester' if __name__ == '__main__': print(A.tmp1) print(A._tmp2)
运行结果:
www hester
__开头和结尾的(__foo__)代表python里特殊方法专用的标识,如 __init__()代表类的构造函数,不建议使用。
4、特殊属性
作用:保存对象的元数据
- __doc__ 用于保存文档字符串
- __name__ 保存类的名称
- __dict__ 保存实例属性名
- __module__ 保存所在模块名
- __base__ 保存类的父类
二、对象具有能动性
三、深入类的属性
同名的类属性与实例属性调用优先级:
以实例名.属性名引用时,优先引用实例属性;以类名.属性名引用时,只能引用类属性。
class A(): a = 0 def __init__(self): self.a = 100 self.b = 200 if __name__ == '__main__': a = A() print(a.a) print(A.a) print(a.b)
运行结果:
100 0 200
在给一个稍微特殊的例子
class A(): count = 10 pass if __name__ == '__main__': a = A() print a.count #result is : 10 a.count = 100 print a.count #result is : 100 print A.count #result is : 10
属性访问的特殊方法(反射),提供用字符串来操作类的属性/方法
- hasattr(obj_bame,’属性名‘)
- setattr(obj_name,'属性名',值)
- getattr(obj_name,’属性名‘)
class A(): a = 0 def __init__(self): self.a = 100 self.b = 200 if __name__ == '__main__': a = A() print(getattr(a,'b')) setattr(a,'b',2000) print(getattr(a,'b')) print(hasattr(a,'cc')) print(hasattr(a,'a'))
运行结果:
200 2000 False True
属性包装
class A(object): def __init__(self): self._weight = 100 self.width = 200 @property def weight(self): return self._weight @weight.setter def weight(self, value): if 0 < value <= 500: self._weight = value else: print('Set Failture!') if __name__ == '__main__': a = A() print(a.weight) a.weight = 1000 print(a.weight)
运行结果:
100 Set Failture! 100
注:类必须继承自object类,包装的变量必须为私有变量_x
看到这里,有些同学可能会觉得使用set、get函数操作变量也可以达到相同的效果,代码如下:
class Studentscore(object): def __init__(self,score): self._score = score def set_score(self,score): self._score = score def get_score(self): return self._score if __name__ == '__main__': s = Studentscore(30) print s.get_score() s._score = 60 print s.get_score()
但如果想将score设置为只读属性,如上代码就无能无力了。property意义便显现出来,代码如下:
class Studentscore(object): def __init__(self,score): self._score = score @property def score(self): return self._score if __name__ == '__main__': s = Studentscore(30) print s.score # s.score = 90 # AttributeError: can't set attribute
外部已无法直接设置score,运行报错。
说句题外话,既然_score已设为私有属性,请大家不要在外部直接使用s._score = xx,这样有违初衷;也说明python的私有属性设置非强制性
property不仅有可读属性,还有可写、删除属性,完整代码如下:
class Studentscore(object): def __init__(self,score): self._score = score @property def score(self): return self._score @score.setter def score(self,score): if not isinstance(score,int): raise ValueError('score must be an integer') if score < 0 or score > 100: raise ValueError('score must between 0 ~ 100') self._score = score @score.deleter def score(self): del self._score if __name__ == '__main__': s = Studentscore(30) print s.score s.score = 90 print s.score # s.score = 120 ValueError('score must between 0 ~ 100')
虚拟属性的例子:
class Studentscore(object): def __init__(self,score): self._score = score self._pass = 60 @property def score(self): return self._score @property def over_score(self): return self._score - self._pass if __name__ == '__main__': s = Studentscore(90) print s.score print s.over_score
运行结果:
90 30
class NoNeg(object): def __init__(self,default = 0): self.default = default def __set__(self,instance,value): if value > 0: self.default = value else: print("The Value must be nonnegative") def __get__(self,instance,owner): return self.default def __delete__(self): pass class Movie(object): rating = NoNeg() score = NoNeg() if __name__ == '__main__': m = Movie() print('rating:',m.rating) print('score:',m.score) m.rating = -4 m.score = 90 print('rating:',m.rating) print('score:',m.score)
运行结果:
('rating:', 0) ('score:', 0) The Value must be nonnegative ('rating:', 0) ('score:', 90)
注:两个类必须为新式类
所有的类成员函数都是非数据描述符:
class Test(): def pr(self): print('Test') if __name__ == '__main__': t = Test() print(dir(t.pr))
运行结果:
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'im_class', 'im_func', 'im_self']
pr函数实现了__get__方法
__call__让类的实例如函数一样可调用:
class Test(): def __call__(self): print('Test calling') if __name__ == '__main__': t = Test() t()
运行结果:
Test calling
同名的实例属性与非数据描述符的访问优先级:
class A(object): def pr(self): print('calling') if __name__ == '__main__': a = A() print(a.pr) a.pr = 10 print(a.pr) del a.pr print(a.pr)
运行结果:
<bound method A.pr of <__main__.A object at 0x022025F0>> 10 <bound method A.pr of <__main__.A object at 0x022025F0>>
说明:同名的实例属性会隐藏(或者说覆盖掉)同名非数据型描述符
四、类方法与静态方法
静态方法:
class Washer(object): def __init__(self,water = 100,scour = 2): self._water = water self.scour = scour @staticmethod def spoons_ml(spoon): return spoon * 0.4 @property def water(self): return self._water @water.setter def water(self,water): if 0 < water <= 500: self._water = water else: print("enter water failture") def add_water(self): print('add water:',self._water) def add_scour(self): print("add scour:",self.scour) def start_wash(self): self.add_water() self.add_scour() print("start wash...") if __name__ == '__main__': #call using class print Washer.spoons_ml(8) #call using instance w1 = Washer() print w1.spoons_ml(8) #using staticmethod to init instance w2 = Washer(300,Washer.spoons_ml(9)) w2.start_wash()
运行结果:
3.2 3.2 ('add water:', 300) ('add scour:', 3.6) start wash...
类方法:
class Washer(object): def __init__(self,water = 100,scour = 2): self._water = water self.scour = scour @staticmethod def spoons_ml(spoons): return spoons * 0.4 @classmethod def get_washer(cls,water,spoons): return cls(water,cls.spoons_ml(spoons)) @property def water(self): return self._water @water.setter def water(self,water): if 0 < water <= 500: self._water = water else: print("enter water failture") def add_water(self): print('add water:',self._water) def add_scour(self): print("add scour:",self.scour) def start_wash(self): self.add_water() self.add_scour() print("start wash...") if __name__ == '__main__': w = Washer.get_washer(60,8) w.start_wash()
运行结果:
('add water:', 60) ('add scour:', 3.2) start wash...
classmethod中三个cls,也可以改为类名;但使用cls更加有优势,更改类名之后,无需更改内部代码。
四、类的继承与方法重载
继承类调用方法、属性顺序,先在继承类中寻找方法、属性;如果没有,向父类中继续寻找;仍然没有,向祖父类中寻找...
多重继承时方法的调用按照深度优先的顺序进行调用
class A(object): def foo(self): print("A foo...") class B(object): def foo(self): print("B foo...") class C(A,B): pass class D(B,A): pass if __name__ == '__main__': c = C() c.foo() d = D() d.foo()
运行结果:
A foo... B foo...
五、类的特殊方法
class A(object): pass #bind a variable tmp = A a = tmp() print a #add attribute to class a.sub = 100 print a.sub #as a function parameter def sum(cls): return cls() print sum(tmp)
运行结果:
<__main__.A object at 0x016526B0> 100 <__main__.A object at 0x01AF2510>
构造一个序列类
需要实现如下部分方法:
1、__len__(self)
2、__getitem__(self,key)
3、__setitem__(self,key,value)
4、__delitem__(self,key)
class MySeq: def __init__(self): self.lseq = ["I","II","III","IV"] def __len__(self): return len(self.lseq) def __getitem__(self,key): if 0 <= key <4: return self.lseq[key] if __name__ == '__main__': m = MySeq() for i in range(4): print(m[i])
运行结果:
I II III IV
自定义一个迭代器
需要实现如下方法:
1、__iter__(self)
2、__next__(self)
class MyIter: def __init__(self,start,end): self.count = start self.end = end def __iter__(self): return self def __next__(self): if self.count < self.end: r = self.count self.count += 1 return r else: raise StopIteration if __name__ == '__main__': for i in MyIter(1,10): print(i)
运行结果:
1 2 3 4 5 6 7 8 9
自定义可比较类
需要实现如下部分方法:
- __gt__() 大于
- __ge__() 大于、等于
- __lt__() 小于
- __le__() 小于、等于
- __eq__() 等于
- __nq__() 不等于
class Point: def __init__(self,x,y): self.x = x self.y = y def __lt__(self,oth): return self.x < oth.x def __gt__(self,oth): return self.y > oth.y if __name__ == '__main__': pa = Point(0,1) pb = Point(1,0) print(pa < pb) print(pa > pb)
运行结果:
True True
自定义可逻辑运行类
需要实现如下部分方法:
- __add__()
- __sub__()
- __mul__()
- __div__()
class Point: def __init__(self,x,y): self.x = x self.y = y def __add__(self,oth): return Point(self.x + oth.x,self.y + oth.y) def init(self): print(self.x,self.y) if __name__ == '__main__': pa = Point(2,3) pb = Point(3,4) pc = pa + pb pc.init()
运行结果:
5 7
六、多态
通过继承实现多态(子类可以作为父类使用),也就是作为同一父类的子类,当父类可作为函数参数时,子类也可以。
class Animal(object): def move(self): print("Animal moving...") class Dog(Animal): pass def move(obj): obj.move() a = Animal() move(a) d = Dog() move(d)
运行结果:
Animal moving... Animal moving...
子类通过重载父类的方法实现多态,也就是子类对同一动作可以有不同反应。
class Animal(object): def move(self): print("Animal moving...") class Cat(Animal): def move(self): print("Cat moving...") def move(obj): obj.move() a = Animal() move(a) c = Cat() move(c)
运行结果:
Animal moving... Cat moving...
python是一种动态语言,变量绑定的类型具有不确定性
a = 1 a = 'abc' a = 1.2
所有函数跟方法可以接受不同类型的参数
def info(obj): print(type(obj)) info(3) info(1.3) info('hester')
调用是否成功取决于参数方法和属性,调用不成功则抛出错误。
def info(obj): obj.move() class Cat(): def move(self): print('Cat moving...') info(Cat()) # info('hester') AttributeError: 'hester' object has no attribute 'move'
所以python中不需要接口
七、类的设计模式
单实例
class myclass: def __new__(cls,*args,**kwargs): if not hasattr(cls,'_name'): cls._name = super().__new__(cls,*args,**kwargs) return cls._name if __name__ == '__main__': mc1 = myclass() mc2 = myclass() print(id(mc1)) print(id(mc2))
运行结果:
7331568 7331568
实现工厂方法
class Ab: a = 1 class Ac: b = 2 class MyFactory: def get_instance(self,ins): return ins() if __name__ == '__main__': mf = MyFactory() print(id(mf.get_instance(Ab))) print(id(mf.get_instance(Ac)))
运行结果:
18276136 18276304
八、实现策略模式
定义:让对象的某个方法可以随时改变,而不用更改其内部代码
-
将类作为参数进行传递
class A: def move(self): print('move...') class B(A): def move(self): print('move on B') class C(A): def move(self): print('move on C') class Moveobj: def set_move(self,A): self.a = A() def move(self): self.a.move() if __name__ == '__main__': m = Moveobj() m.set_move(A) m.move() m.set_move(B) m.move() m.set_move(C) m.move()
运行结果:
move... move on B move on C
实例通过调用set_move方法,可以实现不同的行为;实际使用中,其他的类可以不用继承同样的父类,只要保证有相同的方法即可。
-
将函数作为参数进行传递
def movea(): print('move on a') def moveb(): print('move on b') class Myclass: def set_move(self,obj): self.obj = obj def move(self): self.obj() if __name__ == '__main__': m = Myclass() m.set_move(movea) m.move() m.set_move(moveb) m.move()
运行结果:
move on a move on b
九、类的装饰模式
作用:通过继承可以获得父类的属性,通过重载可以修改其方法;而装饰模式可以不以继承的方式修改其方法,可以不以继承的方式返回一个被修改的类。
核心:装饰器中定义和被装饰类相同名的方法
class Demo: def be_edit_method(self): print('be edit...') def be_keep_method(self): print('be keep...') class Decoroter: def __init__(self,ins): self._ins = ins() def be_edit_method(self): print('start edit...') self._ins.be_edit_method() def be_keep_method(self): self._ins.be_keep_method() if __name__ == '__main__': de = Demo() de.be_edit_method() de.be_keep_method() dc = Decoroter(Demo) dc.be_edit_method() dc.be_keep_method()
运行结果:
be edit... be keep... start edit... be edit... be keep...
装饰器的另一种实现
class Water: def __init__(self): self.name = 'water' def show(self): print(self.name) class Deco: def show(self): print(self.name) class Sugar(Deco): def __init__(self,water): self.name = 'Sugar' self.water = water def show(self): print(self.name) print(self.water.name) class Salt(Deco): def __init__(self,water): self.name = 'Salt' self.water = water def show(self): print(self.name) print(self.water.name) if __name__ == '__main__': w = Water() # w.show() s = Sugar(w) s.show() s = Salt(w) s.show()
运行结果:
Sugar water Salt water
实际中,python已有现成的类装饰器,可以直接使用,代码如下:
def deco(a_class): class NewClass: def __init__(self,age,color): self.wrapped = a_class(age) self.color = color def display(self): print(self.color) print(self.wrapped.age) return NewClass @deco class Cat: def __init__(self,age): self.age = age def display(self): print(self.age) if __name__ == '__main__': c = Cat(4,'black') c.display()
运行结果:
black 4
说明:在函数中自定义类,对传入的类进行修饰,然后返回自定义类