目录
9-3, hasattr()、getattr()、setattr()、delattr()
10-6, __truediv__, __idiv__, __floordiv__方法
14, 使用@property
1, 类
1-1, 什么是类
类是对象的定义, 即对问题进行的行为特征进行建模
新式类和经典类的区别,所有的新式类需要继承一个父类object
1-2, 创建类
class ClassName(bases):
"""class documentation string""" # 类的字符串文档
class_suite # 类体
1-3, 类属性
类属性是在类中定义的变量(是静态变量),与所属的类绑定,与类实例无关
In [35]: class C(object):
...: foo = 100
1-3-1, 查询类属性
1-3-1-1, dir(类名)
通过dir(类名), 查询类属性, 返回一个列表
In [15]: class Myclass(object):
...: 'My class doc'
...: my_version = 1.0
...: def show_my_version(self):
# 注意:方法中访问类属性,只能通过:"类名.类属性"进行访问,否则报错
...: print('version:', Myclass.my_version)
...:
# 通过dir(类名)进行显示, 返回一个列表
In [16]: dir(Myclass)
Out[16]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getstate__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'my_version',
'show_my_version']
1-3-1-2, 类名.__dict__
通过类名.__dict__, 查询类属性, 返回一个mappingproxy, 可通过dict(mappingproxy), 转换为一个字典
# 通过:类名.__dict__进行显示,返回的一个mappingproxy
In [18]: Myclass.__dict__
Out[18]:
mappingproxy({'__module__': '__main__',
'__doc__': 'My class doc',
'my_version': 1.0,
'show_my_version': <function __main__.Myclass.show_my_version(self)>,
'__dict__': <attribute '__dict__' of 'Myclass' objects>,
'__weakref__': <attribute '__weakref__' of 'Myclass' objects>})
In [19]: dict(Myclass.__dict__)
Out[19]:
{'__module__': '__main__',
'__doc__': 'My class doc',
'my_version': 1.0,
'show_my_version': <function __main__.Myclass.show_my_version(self)>,
'__dict__': <attribute '__dict__' of 'Myclass' objects>,
'__weakref__': <attribute '__weakref__' of 'Myclass' objects>}
1-3-1-3, vars(类名)
通过vars(类名), 查询类属性, 结果与类名.__dict__一样
1-3-2, 类的特殊属性
通过类名.__name__, 返回类名
# 返回类名字
In [30]: Myclass.__name__
Out[30]: 'Myclass'
通过类名.__doc__, 返回类字符串文档
# 返回类的字符串文档
In [31]: Myclass.__doc__
Out[31]: 'My class doc'
通过类名.__base__, 返回父类
# 返回类的父类
In [32]: Myclass.__base__
Out[32]: object
通过类名.__bases__, 以元组形式返回所有父类
# 元组形式返回类的父类, 经返回当前类的父类,父类的祖先类不返回
In [33]: Myclass.__bases__
Out[33]: (object,)
通过类名.__module__, 返回类所属的模块名字
# 返回类所属的模块名字
In [34]: Myclass.__module__
Out[34]: '__main__'
1-3-3, 访问类属性
1-3-3-1,类名.类属性访问
通过类名.类属性, 可访问类属性
In [8]: class C(object):
...: foo = 100 # 类属性
# 通过:类名.类属性访问
In [9]: C.foo
Out[9]: 100
1-3-3-2,实例.类属性访问
若不存在同名的实例属性, 可通过实例.类属性访问类属性
# 通过实例属性进行访问, 注意,这里实例自己没有foo属性,才能访问到该类属性
In [59]: m = C()
In [60]: m.foo
Out[60]: 100
# 若实例存在同名的类属性,此时访问的时实例属性
In [75]: m.foo = 200
In [76]: m.foo
Out[76]: 200
1-3-4, 类属性修改
1-3-4-1, 类名.类属性修改
通过类名.类属性 = xxx进行修改
In [67]: class C(object):
...: foo = 100
...: bar = {'a': 1}
In [68]: m = C()
# 通过类名.类属性方式进行修改
In [69]: C.foo = 200
In [70]: C.foo
Out[70]: 200
1-3-4-2, 实例.类属性修改
若类属性是可变对象, 可以通过实例名.类属性进行修改
In [77]: class C(object):
# 类属性是可变对象
...: bar = {'a': 1}
In [78]: m = C()
# 通过"实例.类属性=xxx"进行修改, 结果映射到类属性上
# 此时的类属性需要为可变类型
# 若是不可变类型通过"实例.类属性=xxx"相当于创建一个与类属性同名的实例属性
In [79]: m.bar['b'] = 10
In [80]: C.bar
Out[80]: {'a': 1, 'b': 10}
1-4, 方法
1-4-1, 实例方法
实例方法为类中定义的带有self参数的函数, self表示实例本身, 通过"实例名.方法()"进行调用
In [12]: class Myclass(object):
# 该函数为类的一个方法,即类的一个属性
...: def my_no_acti_method(self):
...: pass
In [13]: m = Myclass()
# 调用实例方法
In [14]: m.my_no_acti_method()
1-4-2, 类方法
在类中定义的方法,需要类作为方法的第一个参数以及通过@classmethod进行修饰
class TestClassMethod(object):
# 通过@classmethod装饰foo是一个类方法, 需要一个cls参数,cls表示类
@classmethod
def foo(cls):
print("calling class method foo()")
print("foo() is part of class:", cls.__name__)
if __name__ == "__main__":
# 类方法可以通过实例和类名进行调用
tcm = TestClassMethod()
TestClassMethod.foo()
tcm.foo()
# 输出结果
calling class method foo()
foo() is part of class: TestClassMethod
calling class method foo()
foo() is part of class: TestClassMethod
1-4-3, 静态方法
在类中定义的方法,不带self参数,需要通过@staticmethod进行修饰,不然会报错
class TestStaticMethod(object):
# 通过@staticmethod装饰foo是一个静态方法, 不需要参数
@staticmethod
def foo():
print("calling static method foo()")
if __name__ == "__main__":
# 静态方法可以通过实例和类名进行调用
tms = TestStaticMethod()
TestStaticMethod.foo()
tms.foo()
# 输出结果
calling static method foo()
calling static method foo()
2, 实例
2-1, 什么是实例
实例是类的具体对象
2-2, 创建实例
通过实例名称=类名(), 可创建了一个实例
In [35]: class Myclass(object):
...: pass
# 创建实例m
In [36]: m = Myclass()
2-3, __init__方法
创建实例时, __init__方法第一个被调用,常用来完成一些初始化,如参数赋值
若类中没有定义__init__方法, 实例化时,会调用基类的__init__方法
若类中定义了__init__方法, 实例化时,会覆盖基类的__init__方法
实例化时,传递给类的参数,都是传递给__init__方法
__init__返回没有返回值
2-4, __new__方法
通过__new__方法, 对内建函数(如float不可变对象)进行派生,返回一个实例
__new__是一个类方法
class RoundFloat(float):
def __new__(cls, *args, **kwargs):
return super(RoundFloat, cls).__new__(cls, round(args[0], 2))
if __name__ == "__main__":
print(RoundFloat(1.2365))
2-5, 实例属性
2-5-1, 设置属性
设置实例属性, 一是在类的__init__进行设置, 二是在方法中设置,三是在实例创建后动态设置
实例属性仅针对响应的实例生效, 不会影响到其他新创建的实例对象
class MyClass(object):
# 在__init__方法中设置实例属性
def __init__(self, a, b):
self.a = a
self.b = b
def my_method(self):
# 在方法中设置实例属性
self.c = 3
if __name__ == "__main__":
m = MyClass(1, 2)
# 实例化后设置实例属性
m.d = 10
2-5-2, 查询实例属性
2-5-2-1, dir(实例名)
通过dir(实例名), 以列表形式返回实例的所有属性
# 通过dir(实例名)查询
In [43]: class MyClass(object):
...: def __init__(self, a, b):
...: self.a = a
...: self.b = b
...: def my_method(self):
...: self.c = 3
In [44]: m = MyClass(1, 2)
In [45]: dir(m)
Out[45]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getstate__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'a',
'b',
'my_method']
2-5-2-2, 实例名.__dict__
通过实例名.__dict__, 以字典形式返回实例的属性
# 通过:实例名称.__dict__查询,返回一个字典
In [47]: m.__dict__
Out[47]: {'a': 1, 'b': 2}
2-5-2-2, vars(实例名)
通过vars(实例名), 返回的结果与实例名.__dict__一致
2-5-2, 实例的特殊属性
通过m.__class__, 返回实例的类名
# 返回实例的类
In [49]: m.__class__
Out[49]: __main__.MyClass
2-5-3, 访问实例属性
通过实例名.属性名, 访问实例属性
# 访问实例属性
In [66]: m.a
Out[66]: 1
若实例属性与类属性同名时,则通过实例.属性进行访问和修改时,实际操作的是实例的属性,因为类的属性被同名的实例属性遮挡
# 实例属性遮挡类属性
In [81]: class C(object):
...: bar = dict(a=1)
In [82]: m = C()
In [83]: m.bar = dict(a=1)
In [85]: m.bar['b'] = 10
# 实际操作体现到实例属性上
In [86]: m.bar
Out[86]: {'a': 1, 'b': 10}
# 类属性没有发生变化,因为同名的实例属性遮挡了类属性
In [87]: C.bar
Out[87]: {'a': 1}
3, 实例属性查询流程
通过实例名.属性访问属性时,先在实例属性名称空间查询,若没有再到类的属性名称空间查找,若没有再到父类的类名称空间查找;先找到了就不在继续查找
4, 类属性的查询流程
访问一个类的属性时,先在当前类的__dict__中查找,再到父类的__dict__中查找, 即深度优先原则, 父类中的搜索是从左到右进行
5, 绑定方法
通过实例来调用类的方法,叫着调用绑定方法
6, 非绑定方法
通过类名来调用类的方法,叫着调用非绑定方法,此时必须显示的传入self参数
class AddBookEntry(object):
def __init__(self, nm, ph):
self.nm = nm
self.ph = ph
class EmplAddrBookEntry(AddBookEntry):
def __init__(self, nm, ph, em, id):
# 这里是通过"父类.__init__()"调用,为非绑定方法调用
# 调用非绑定方法时,self需要显示传入
AddBookEntry.__init__(self, nm, ph)
self.empid = id
self.email = em
7, 组合
组合就是让不同的类混合到一起,增加代码的复用性
当类之间有显著不同,且较小的类是较大类组件时,组合表现得很好
class Name(object):
def __init__(self, nm):
self.nm = nm
class Phone(object):
def __init__(self, ph):
self.ph = ph
class NewAddrBookEntry(object):
def __init__(self, nm, ph):
#类实例作为其属性
self.name = Name(nm)
self.phone = Phone(ph)
print("created instance for:", self.name)
if __name__ == "__main__":
NewAddrBookEntry('zhangsan', '123456789')
# 输出
created instance for: <__main__.Name object at 0x0000028BCFBE8990>
8, 继承
除父类的字符串文档,子类会继承父类的所有属性
8-1, 子类继承父类方法
# ParenClass1[...,ParenClassN:为父类的名称,父类可以是一个或多个
# 多个父类使用逗号进行隔开
class SubClassName(ParenClass1[...,ParenClassN]):
class_suite
class Parent(object):
def parent_method(self):
print("calling parent method")
# Child继承父类Parent
class Child(Parent):
def child_method(self):
print("calling child method")
if __name__ == "__main__":
c = Child()
# 子类会继承父类的属性
c.parent_method()
c.child_method()
8-3, 通过继承覆盖方法
当子类存在与父类同名的方法时,父类的方法会被覆盖
class Parent(object):
"""Parent doc"""
def __init__(self):
print("calling Parent's construtor")
def foo(self):
print("calling Parent foo()")
class Child(Parent):
# 实例化时, 这里的__init__会覆盖父类的__init__方法
def __init__(self):
# 调用父类的__init__方法, 继承父类__init__定义的属性
super(Child, self).__init__()
print("calling Child's construtor")
def foo(self):
print("calling Child foo()")
if __name__ == "__main__":
c = Child()
# 输出结果
# 由于子类定义了__init__覆盖了父类的__init__,所以调用子类的__init__
calling Child's construtor
--------------------------------------------------------------------------
if __name__ == "__main__":
# 父类的字符串文档不会被子类继承
print('c.__doc__:', Child.__doc__)
# 输出结果: 由于__doc__文档不会继承,所以这里输出None
c.__doc__: None
--------------------------------------------------------------------------
if __name__ == "__main__":
# 子类的与父类同名的方法,会覆盖父类的方法
c.foo()
# 输出结果
calling Child foo()
--------------------------------------------------------------------------
if __name__ == "__main__":
# 通过非绑定方法的方式,调用父类foo,实例需要显示传入
Parent.foo(c)
# 输出结果
calling Parent foo()
8-4, 从标准类型派生
8-4-1, 从不可变类型派生
从不可变类型派生时,需要在子类中定义__new__方法来实现
# __new__返回的是一个实例对象
class RoundFloat(float):
# 在子类中定义__new__, 通过super调用父类的__new__属性
def __new__(cls, *args, **kwargs):
return super(RoundFloat, cls).__new__(cls, round(args[0], 2))
8-4-2, 从可变类型派生
class SortedKeyDict(dict):
def keys(self):
# 通过super调用父类dict的keys()方法
return sorted(super(SortedKeyDict, self).keys())
if __name__ == "__main__":
print(RoundFloat(1.2365))
d = SortedKeyDict((('zheng-cai', 67), ('hui-jun', 68), ('xin-yi', 2)))
print("By iterator:".ljust(12), [key for key in d])
print("By keys():".ljust(12), d.keys())
8-5, 多重继承
当子类存在多个父类时,这种继承就叫多重继承
8-5-1, 方法解释顺序(MRO)
经典类使用深度优先, 新式类使用广度优先
9, 内建函数
9-1, issubclass()
通过issubclass(sub, (sup1, ..., supn)), 用于判读sub类是否是(sup1, ..., supn)这个元组中某个类的子类,是返回True,否则返回False
In [20]: class P1(object):
...: pass
In [21]: class P2(object):
...: pass
In [22]: class C1(P1):
...: pass
# 检查C1是不是P1的子类
In [23]: issubclass(C1, P1)
Out[23]: True
# 检查C1是不是P2的子类
In [24]: issubclass(C1, P2)
Out[24]: False
# 检查C1是不是元组中某个类的子类
In [25]: issubclass(C1, (P2, P1))
Out[25]: True
9-2, isinstance()
通过isinstance(obj1, (class1, ..., classn), 判断obj1是否是(obj2, ..., objn)元组中某个类的实例,是返回True,否则返回False
In [29]: class C1(object):
...: pass
In [30]: class C2(object):
...: pass
In [31]: c1 = C1()
In [32]: c2 = C2()
In [33]: isinstance(c1, C1)
Out[33]: True
In [34]: isinstance(c1, (C1, C2))
Out[34]: True
In [36]: isinstance(c1, C2)
Out[36]: False
9-3, hasattr()、getattr()、setattr()、delattr()
使用以上方法时,属性名称需要用引号括起来
通过hasattr(obj, 'attr'), 检查对象obj是否具备attr属性,存在返回True,不存在返回False
In [52]: class MyClass(object):
...: def __init__(self):
...: self.foo = 100
In [53]: m = MyClass()
# 注意,属性名需要用引号括起来
In [54]: hasattr(m, 'foo')
Out[54]: True
In [55]: hasattr(m, 'foo1')
Out[55]: False
通过getattr(obj, 'attr', default), 用于获取obj的属性attr
In [52]: class MyClass(object):
...: def __init__(self):
...: self.foo = 100
# 获取m的属性'foo', 等价于m.foo
In [59]: getattr(m, 'foo')
Out[59]: 100
# 若获取的属性不存在,且没有设置默认中,则报错
In [57]: getattr(m, 'foo1')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[57], line 1
----> 1 getattr(m, 'foo1')
AttributeError: 'MyClass' object has no attribute 'foo1'
# 若获取的属性不存在,则返回默认值
In [58]: getattr(m, 'foo1', 'opps')
Out[58]: 'opps'
通过setattr(obj, 'attr', value), 设置obj的属性attr, 若attr存在,则修改原来的值;若不存在,则为obj添加attr属性
In [60]: class MyClass(object):
...: def __init__(self):
...: self.foo = 100
In [61]: m = MyClass()
# 设置m.foo1 = 'haha'
In [62]: setattr(m, 'foo1', 'haha')
# 设置m.foo = 'hehe'
In [63]: setattr(m, 'foo', 'hehe')
In [64]: m.foo
Out[64]: 'hehe'
In [65]: m.foo1
Out[65]: 'haha'
通过delattr(obj, 'attr'), 用于删除对象的属性attr
In [66]: class MyClass(object):
...: def __init__(self):
...: self.foo = 100
In [67]: m = MyClass()
# 删除实例m的foo属性
In [68]: delattr(m, 'foo')
9-4, super()
通过super(类名, self), 用于查找类的父类,以方便调用相关的属性
9-5, dir()
通过dir(obj), 用来显示obj具有哪些属性,返回一个列表;若没有提供obj,则返回当前环境所有变量
9-6, vars()
通过vars(obj), 用来显示obj具有哪些属性,返回一个字典;obj需要具备__dict__属性,否则会报错;若没有提供参数,则返回值与locals一致
10, 用特殊方法定制类
10-1,__str__方法
类中定义__str__方法,可自定义显示出实例的具体值,而不是实例对象本身
# 类中未定义__str__方法
In [11]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
# 由于该类没有定义__str__方法,调用print时,显示的是实例对象本身
In [12]: print(RoundFloatManual(1.23))
<__main__.RoundFloatManual object at 0x00000239BCF442D0>
In [13]: str(RoundFloatManual(1.23))
Out[13]: '<__main__.RoundFloatManual object at 0x00000239BCF44D10>'
# 类中定义__str__方法
In [14]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
...: # 类中定义__str__方法
# 在通过print(实例)和str(实例)时,调用如下方法,返回对应的值
...: def __str__(self):
...: return str(self.num)
# 使用print时,调用了__str__, 得到如下输出
In [15]: print(RoundFloatManual(1.23))
1.23
# 使用str时,调用了__str__, 得到如下输出
In [16]: str(RoundFloatManual(1.23))
Out[16]: '1.23'
10-2,__repr__方法
效果与__str__差不多, 类中定义了该方法,可自定义显示出实例的具体值,而不是实例对象本身
In [1]: class RoundFloatManaul(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
# 通过__repr__自定义类的输出
...: def __repr__(self):
...: return repr(self.num)
In [4]: RoundFloatManaul(1.2343)
Out[4]: 1.23
10-3, __add__方法, __iadd__方法
通过__add__, __iadd__方法可以让对象之间进行加法运算, __add__中以实例对象返回,__iadd__中以实例本身self返回
In [9]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
...:
...: def __str__(self):
...: return str(self.num)
...:
...: __repr__ = __str__
...:
# 对象之间可以进行加法运算
# 注意结果返回的是一个实例
...: def __add__(self, other):
...: self.num = self.num + other.num
...: return self.__class__(self.num)
...:
# 对象之间可以进行自加运算
# 注意最后返回是的实例本身
...: def __iadd__(self, other):
...: self.num += other.num
...: return self
In [10]: RoundFloatManual(1.2343) + RoundFloatManual(1.236)
Out[10]: 2.47
# 时间格式向加
In [53]: class Time60(object):
...: def __init__(self, hr, minu):
...: self.hr = hr
...: self.minu = minu
...:
...: def __str__(self):
...: return '%d:%.02d' % (self.hr, self.minu)
...:
...: __repr__ = __str__
...:
...: def __add__(self, other):
...: self.hr = self.hr + other.hr
...: self.minu = self.minu + other.minu
...: self.more_hr, self.minu = divmod(self.minu, 60)
...: self.hr = self.hr + self.more_hr
...:
...: return self.__class__(self.hr, self.minu)
...:
...: def __iadd__(self, other):
...: self.hr += other.hr
...: self.minu += other.minu
...: self.more_hr, self.minu = divmod(self.minu, 60)
...: self.hr += self.more_hr
...:
...: return self
...:
In [54]: a = Time60(10, 40)
In [55]: b = Time60(10, 40)
In [56]: print(Time60(10, 40) + Time60(10, 40))
21:20
In [57]: a += b
In [58]: print('a:', a)
a: 21:20
10-4, __sub__, __isub__方法
通过__sub__, __isub__方法, 可以让对象之间进行减法运算, __sub__中以实例对象返回,__isub__中以实例本身self返回
In [11]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
...:
...: def __str__(self):
...: return str(self.num)
...:
...: __repr__ = __str__
...:
# 定义类的减法运算, 最后返回的是实例
...: def __sub__(self, other):
...: return self.__class__(self.num - other.num)
...:
# 定义类的自减运行,最后返回实例本身
...: def __isub__(self):
...: self.num += self.num
...: return self
In [13]: RoundFloatManual(1.2343) - RoundFloatManual(1.215)
Out[13]: 0.01
In [14]: a = RoundFloatManual(1.2365)
In [15]: b = RoundFloatManual(0.1566)
In [16]: a -= b
In [17]: print('a:', a)
10-5, __mul__, __imul__方法
通过__mul__, __imul__方法, 可以让对象之间进行乘法运算, __mul__中以实例对象返回,__imul__中以实例本身self返回
In [18]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
...:
...: def __str__(self):
...: return str(self.num)
...:
...: __repr__ = __str__
...:
# 定义对象可以进行剩法运算,返回的是实例
...: def __mul__(self, other):
...: return self.__class__(self.num * other.num)
...:
# 定义对象可以进行自剩运算,返回的是实例
...: def __imul__(self, other):
...: self.num *= self.num
...: return self
In [19]: a = RoundFloatManual(1.236)
In [20]: b = RoundFloatManual(0.1566)
In [21]: a * b
Out[21]: 0.2
In [22]: a *= b
In [23]: a
Out[23]: 1.5376
10-6, __truediv__, __idiv__, __floordiv__方法
通过__truediv__, __idiv__, __floordiv__方法,可以让对象之间实现除法运算,
注意要实现出发功能,不能单独只定义__idiv__方法,需要与__truediv__同时定义
In [70]: class RoundFloatManual(object):
...: def __init__(self, num):
...: assert isinstance(num, float), 'num must be float'
...: self.num = round(num, 2)
...:
...: def __str__(self):
...: return str(self.num)
...:
...: __repr__ = __str__
...:
# 定义除法功能
...: def __truediv__(self, other):
...: return self.__class__(self.num / other.num)
...:
# 定义自除功能,需要与__truediv__同时定义,才会生效
...: def __idiv__(self, other):
...: self.num /= other.num
...:
...: return self
...:
# 定义地板除功能
...: def __floordiv__(self, other):
...: return self.__class__(self.num // other.num)
In [71]: a = RoundFloatManual(3.212)
In [72]: b = RoundFloatManual(2.212)
In [73]: a = a / b
In [74]: print('result:', a)
result: 1.45
In [75]: a /= b
In [76]: a
Out[76]: 0.66
In [77]: a //b
Out[77]: 0.0
10-7, __iter__方法
通过__iter__方法, 可以让定制类支持可迭代功能
In [98]: class RandSeq(object):
...: def __init__(self, seq):
...: self.seq = seq
...:
# 该方法表示类支持可迭代功能
...: def __iter__(self):
...: return self
...:
...: def __next__(self):
...: return choice(self.seq)
In [100]: m = RandSeq((1, 2, 3))
In [101]: next(m)
Out[101]: 2
In [102]: next(m)
Out[102]: 1
# 返回指定项的迭代器
In [23]: class AnyIter(object):
...: def __init__(self, seq, flag=False):
...: self.seq = iter(seq)
...: self.flag = flag
...:
...: def __iter__(self):
...: return self
...:
# howmany用于指定返回迭代器的项目数量
...: def next(self, howmany):
...: my_list = []
...: for i in range(howmany):
...: try:
...: my_list.append(next(self.seq))
...: except StopIteration:
...: if self.flag:
...: break
...: else:
...: raise
...:
...: return my_list
...:
In [24]: a = AnyIter(range(10), flag=True)
# 返回3项
In [25]: a.next(3)
Out[25]: [0, 1, 2]
# 返回10项
In [27]: a = AnyIter(range(10), flag=True)
In [28]: a.next(10)
Out[28]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
10-8,__slots__方法
通过__slots__方法, 可以在定义类时,限制实例的属性
In [65]: class MyClass(object):
...: def __init__(self, a, b):
...: self.a = a
...: self.b = b
...:
# 限制类只有如下两个属性, 属性名需要用单引号括起来
...: __slots__ = ('a', 'b')
In [66]: m = MyClass('haha', 'hehe')
In [67]: m.a
Out[67]: 'haha'
In [68]: m.b
Out[68]: 'hehe'
# 因为已经通过__slots__限制了类的属性,所以不能再动态创建类的属性了
In [69]: m.c = 1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[69], line 1
----> 1 m.c = 1
# 在类中使用了__slots__后,实例是不具备__dict__属性的
In [70]: m.__dict__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[70], line 1
----> 1 m.__dict__
AttributeError: 'MyClass' object has no attribute '__dict__'
10-9, __getattribute__方法
__getattribute__是属性访问拦截器,只有类继承object,就默认存在属性拦截器, 访问实例属性时,就会调用该方法, 返回结果为该属性的值,
In [1]: class Myclass(object):
...: def __init__(self):
...: self.c = 'aaaaaaaa'
...:
# item为属性对应的键
# 这里的作用是覆盖object的__getattribute__方法
...: def __getattribute__(self, item):
...: print('自定义打印')
...: return super(Myclass, self).__getattribute__(item)
In [2]: m = Myclass()
# m.c操作的流程:属性值先传给__getattribute__函数,经过处理后返回对应的属性值
In [3]: print('m.a:', m.c)
自定义打印
m.a: aaaaaaaa
# 该类定义会有问题
class Myclass(object):
def __init__(self):
self.c = 'aaaaaaaa'
def func(self):
return 'attr error'
def __getattribute__(self, item):
print('自定义打印')
# 注意这里是键需要用括号括起来
if item == 'c':
return super(Myclass, self).__getattribute__(item)
else:
# 当访问的属性不是c是,调用self.func()会进入无穷递归,从而出错
# 因为self.func()也会调用__getattribute__
return self.func()
11, 属性私有化
在属性前面加上两个下滑线(self.__xxx),这样的属性不能从外部对齐进行访问
In [29]: class MyClass(object):
...: def __init__(self, a, b):
# 在类中,加两个下划线的属性为私有属性,不能再类外部进行访问
...: self.__a = a
...: self.__b = b
...:
...: def get_attr_a(self):
...: return self.__a
...:
...: def get_attr_b(self):
...: return self.__b
In [30]: m = MyClass('haha', 'hehe')
# 外部访问私有属性报错
In [31]: m.__a
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[31], line 1
----> 1 m.__a
AttributeError: 'MyClass' object has no attribute '__a'
# 通过类的方法进行访问
In [32]: m.get_attr_a()
Out[32]: 'haha'
# 外部访问类私有属性的方法
In [33]: m._MyClass__a
Out[33]: 'haha'
12, 包装
包装就是对一个对象的功能进行修改,通过授权的方式来为对象添加新的功能,授权的关键点是覆盖__getattr__方法
In [56]: class WrapMe(object):
...: def __init__(self, obj):
...: self.__obj = obj
...:
...: def __str__(self):
...: return str(self.__obj)
...:
...: __repr__ = __str__
...:
# 当实例属性,在实例和类的名字空间中不存在时,调用该方法查询属性
...: def __getattr__(self, item):
...: return getattr(self.__obj, item)
...: # 由于部分特殊特殊行为没有在实例的属性中,需要通过调用该方法返回原始对象进行操作
...: def get(self):
...: return self.__obj
In [57]: m = WrapMe(1-2j)
# m对象是没有real属性的,是通过__getattr__方法获取到的属性
In [58]: m.real
Out[58]: 1.0
# m对象是没有imag属性的,是通过__getattr__方法获取到的属性
In [59]: m.imag
Out[59]: -2.0
# m对象是没有conjugate()属性的,是通过__getattr__方法获取到的属性
In [60]: m.conjugate()
Out[60]: (1+2j)
# m对象是没有切片和索引行为,通过调用get方法,返回源对象后进行的操作
In [61]: WrapMe(range(5)).get()[3]
Out[61]: 3
In [62]: WrapMe(range(5)).get()[:3]
Out[62]: range(0, 3)
13, 描述符
描述只针对于新式类而言,只要在新式类中实现了__get__, __set__, __delete__(该方法很少用),该类就是一个描述符
描述符分数据描述符和非数据描述符:实现了__get__, __set__方法的类为数据描述符,
没有实现__set__方法的类为非数据描述符
描述符以实例形式作为类的一个类属性
13-1, 非数据描述符
# 该类没有实现__set__方法,所以是一个非数据描述符
In [24]: class Desc(object):
...: def __get__(self, instance, owner):
...: print("call __get__() ...")
...: print('self:', self)
...: print('instance:', instance)
...: print('owner:', owner)
...:
...:
...: class TestDesc(object):
# 描述符以实例形式作为一个类属性
...: x = Desc()
In [25]: m = TestDesc()
# m.x:先到实例中查询x属性
# 由于实例属性中没有x属性,再到类中查找,类中有x属性,且是一个描述符
# 所以访问该属性调用的是__get__方法
In [26]: m.x
call __get__() ...
self: <__main__.Desc object at 0x0000016F565B2290>
instance: <__main__.TestDesc object at 0x0000016F564CD690>
owner: <class '__main__.TestDesc'>
13-2, 数据描述符
# 该类实现了__get__,__set__,所以为数据描述符
In [32]: class Desc(object):
...: def __get__(self, instance, owner):
...: print("call __get__() ...")
...: print('self:', self)
...: print('instance:', instance)
...: print('owner:', owner)
...:
...: def __set__(self, instance, value):
...: self.value = value
In [33]: class TestDesc(object):
# 描述符以实例形式作为一个类属性
...: x = Desc()
In [34]: m = TestDesc()
In [35]: m.x = 10
# 输出说明
# 当实例属性与类属性同名时, 且类属性是一个数据描述符,此时数据描述符优先级大于实例描述符
# 所以m.x输出是调用__get__
In [36]: m.x
call __get__() ...
self: <__main__.Desc object at 0x0000016F5520F1D0>
instance: <__main__.TestDesc object at 0x0000016F55134D10>
owner: <class '__main__.TestDesc'>
# 由于y只有实例属性中存在,所以m.y输出实例的属性值
In [37]: m.y = 20
In [38]: m.y
Out[38]: 20
13-3, 实例属性的优先级大于非数据描述符
# 该描述符只定义了__get__方法,是非数据描述符
In [39]: class Desc(object):
...: def __init__(self, val=None):
...: self.val = val
...:
...: def __get__(self, instance, owner):
...: print("call __get__() ...")
...: print('self:', self)
...: print('instance:', instance)
...: print('owner:', owner)
...:
...: return self.val
...:
...:
...: class TestDesc(object):
...: x = Desc('haha')
In [40]: t = TestDesc()
# 此时是调用的是__get__方法
In [41]: t.x
call __get__() ...
self: <__main__.Desc object at 0x0000016F56966490>
instance: <__main__.TestDesc object at 0x0000016F5694BC10>
owner: <class '__main__.TestDesc'>
Out[41]: 'haha'
# 由于实例属性的优先级大于非数据描述符,所以这里输出的是实例的属性值
In [42]: t.x = 'hehe'
In [43]: t.x
Out[43]: 'hehe'
13-4, 属性的优先级
先是数据描述符,再是实例属性,再是非数据描述符,最后是__getattr__中的属性
14, 使用@property
在类中使用@property,可以将类的方法通过“实例.方法名”的方式来调用
In [32]: class Screen(object):
# 设置实例属性查询功能
...: @property
...: def width(self):
# 注意:self.__width的名称不要方法同名,
# 如果使用self.width表示又继续调用width方法,造成无穷递归
...: return self.__width
...:
# 设置实例属性修改功能
...: @width.setter
...: def width(self, val):
...: self.__width = val
...:
...: @property
...: def length(self):
...: return self.__length
...:
...: @length.setter
...: def length(self, val):
...: self.__length = val
...:
...: @property
...: def resolution(self):
...: return self.width * self.length
...:
In [33]: s = Student()
# 通过实例.方法名的方式来调用实例的方法
In [34]: s.width = 100
In [35]: s.length = 100
In [36]: s.width * s.length
Out[36]: 10000