第25章-OOP:宏伟蓝图
为何使用类
与其他程序组成单元相比,类有三个独到之处:
- 多重实例:类基本上就是生产对象的工厂,每次调用一个类,都有独立命名空间的新对象。
- 通过继承进行定制:类也支持OPP的继承的概念。
- 运算符重载:通过提供特定的协议方法,类可以重载运算符。
第26章-类代码编写基础
类产生多个实例
- 类对象和实例对象各自有独立的命名空间。
- 说白了类就是实例的工厂函数
类对象提供默认行为
- class语句创建类对象并将其复制给变量名
- class语句内的赋值语句会创建类的属性
- 类属性提供对象的状态和行为
实例是具体的元素
- 像函数那样调用类对象会创建新的实例对象
- 每个实例对象继承类的属性并获得了自己的命名空间
- 在方法内怼self属性做赋值运算会产生每个实例自己的属性
注意:
- 可以对实例的直接赋值
类可以截获python运算符
- 以双下划线命名的方法都是特殊钩子,可以用来拦截运算
- 当实例出现在内置运算时,这类方法会自动调用(指上述双下划线的方法)
- 类可以覆盖多数内置类型运算
- 运算符覆盖方法没有默认值,而且也不需要
- 运算符可让类与python的对象模型相继承
In [294]: class rec:pass
In [295]: rec.name='damao'
In [296]: rec.age=18
In [297]: rec.name
Out[297]: 'damao'
In [298]: x=rec()
In [299]: x.age
Out[299]: 18
In [300]: x.name
Out[300]: 'damao'
In [301]: rec.__dict__
Out[301]:
mappingproxy({'__module__': '__main__',
'__dict__': ,
'__weakref__': ,
'__doc__': None,
'name': 'damao',
'age': 18})
In [302]: x.__dict__
Out[302]: {}
第27章 更多实例
第28章 类代码编写细节
- class是创建并且隐式赋值,并不是声明
- 注意。类可以通过直接赋值来改变属性值的。
In [303]: class ShareData:
...: spam=42
...:
In [304]: x=ShareData()
In [305]: y=ShareData()
In [306]: x.spam
Out[306]: 42
In [307]: y.spam
Out[307]: 42
In [308]: ShareData.spam=24
In [309]: x.spam
Out[309]: 24
In [310]: y.spam
Out[310]: 24
In [311]: x.spam=100
In [312]: x.spam
Out[312]: 100
In [313]: y.spam
Out[313]: 24
In [314]: ShareData.spam
Out[314]: 24
注意,在构造时,python只会找出并且调用一个
__init__
类的方法上如果使用了@abstractmethod,那么这个类不能被实例话,它的子类如果没有实现fun方法,也不能被实例化。
# 其中11是模块属性,22是函数内本地变量,33是类属性,44是方法中本地变量,55是实例属性
In [320]: X=11
...:
...: def f():
...: print(X)
...:
...: def g():
...: X=22
...: print(X)
...:
...: class C:
...: X=33
...: def m(self):
...: X=44
...: self.X=55
...:
In [321]:
In [321]: print(X)
11
In [322]: f()
11
In [323]: g()
22
In [324]: print(X)
11
In [325]: obj=C()
In [326]: obj.X
Out[326]: 33
In [327]: obj.m()
In [328]: obj.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 obj.x
AttributeError: 'C' object has no attribute 'x'
In [329]: obj.X
Out[329]: 55
In [330]: C.X
Out[330]: 33
In [331]: C.m.X
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 C.m.X
AttributeError: 'function' object has no attribute 'X'
In [332]: g.X
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
----> 1 g.X
AttributeError: 'function' object has no attribute 'X'
In [333]: obj.m.__dict__
Out[333]: {}
# 打印继承树
def classtree(cls, indent):
print('.' * indent, cls.__name__)
for supercls in cls.__bases__:
classtree(supercls, indent + 3)
def instancetree(inst):
print('Tree of %s' % inst)
classtree(inst.__class__, 3)
def selftest():
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E: pass
class F(D, E): pass
instancetree(B())
instancetree(F())
第29章 运算符重载
运算符的重载其实就是一种拦截。
# 定义__getitem__之后立马就支持了for循环
In [381]: class stepper:
...: def __getitem__(self, item):
...: return self.data[item]
...:
...: X=stepper()
...:
...: X.data='Spam'
...:
...:
In [382]: X[1]
Out[382]: 'p'
In [383]: for item in X:
...: print(item)
...:
S
p
a
m
python中所有的迭代环境都会先尝试__iter__
方法,再尝试__getiterm__
方法。iter方法是返回一个迭代器,而getiterm方法是获取偏移量重复索引。
In [384]: class Squares:
...: def __init__(self, start, stop):
...: self.value = start - 1
...: self.stop = stop
...: # 返回本身,所以这种方法只支持一个迭代器
...: def __iter__(self):
...: return self
...:
...: def __next__(self):
...: if self.value == self.stop:
...: raise StopIteration
...: self.value += 1
...: return self.value ** 2
...:
In [385]:
In [385]: for i in Squares(1,5)
File "", line 1
for i in Squares(1,5)
^
SyntaxError: invalid syntax
In [386]: for i in Squares(1,5):
...: print(i)
...:
1
4
9
16
25
In [387]: list(Squares)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 list(Squares)
TypeError: 'type' object is not iterable
In [388]: list(Squares(1,6))
Out[388]: [1, 4, 9, 16, 25, 36]
In [389]: X=Squares(1,5)
In [390]: I=iter(X)
In [391]: next(I)
Out[391]: 1
In [392]: next(I)
Out[392]: 4
In [393]: next(I)
Out[393]: 9
In [394]: next(I)
Out[394]: 16
In [395]: next(I)
Out[395]: 25
In [396]: next(I)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
in ()
----> 1 next(I)
in __next__(self)
9 def __next__(self):
10 if self.value == self.stop:
---> 11 raise StopIteration
12 self.value += 1
13 return self.value ** 2
StopIteration:
- 双下划线开头的变量名会自动扩张,比如
__spam
,将扩展成_x__spam
In [400]: class C1:
...: def meth1(self):self.__X=88
...: def meth2(self):print(self.__X)
...:
...: class C2:
...: def metha(self):self.__X=99
...: def methb(self):print(self.__X)
...:
...: class C3(C1,C2):pass
...:
...:
In [401]: I=C3()
In [402]:
In [402]: I.meth1()
In [403]: I.metha()
# 注意这个地方 值没有被覆盖了,跟下面做对比
In [404]: I.__dict__
Out[404]: {'_C1__X': 88, '_C2__X': 99}
In [405]: I.meth2()
88
In [406]: I.methb()
99
In [407]: class C1:
...: def meth1(self):self.X=88
...: def meth2(self):print(self.X)
...:
...: class C2:
...: def metha(self):self.X=99
...: def methb(self):print(self.X)
...:
...: class C3(C1,C2):pass
...:
...: I=C3()
...:
...:
In [408]: I.meth1()
In [409]: I.metha()
# 注意这个地方 值被覆盖了
In [410]: I.__dict__
Out[410]: {'X': 99}
In [411]: I.meth2()
99
In [412]: I.methb()
99