Python --面向对象编程

目录

1, 类

1-1, 什么是类

1-2, 创建类

1-3, 类属性

1-3-1, 查询类属性

1-3-1-1, dir(类名)

1-3-1-2, 类名.__dict__

1-3-1-3, vars(类名)

1-3-2, 类的特殊属性

1-3-3, 访问类属性

1-3-3-1,类名.类属性访问 

1-3-3-2,实例.类属性访问 

1-3-4, 类属性修改

1-3-4-1, 类名.类属性修改

1-3-4-2, 实例.类属性修改

1-4, 方法

1-4-1, 实例方法

1-4-2, 类方法

1-4-3, 静态方法

2, 实例

2-1, 什么是实例

2-2, 创建实例

2-3, __init__方法

2-4, __new__方法

2-5, 实例属性

2-5-1, 设置属性

2-5-2, 查询实例属性

2-5-2-1, dir(实例名)

2-5-2-2, 实例名.__dict__

2-5-2-2, vars(实例名)

2-5-2, 实例的特殊属性

2-5-3, 访问实例属性

3, 实例属性查询流程

4, 类属性的查询流程

5, 绑定方法

6, 非绑定方法

7, 组合

8, 继承

8-1, 子类继承父类方法

8-3, 通过继承覆盖方法

8-4, 从标准类型派生

8-4-1, 从不可变类型派生

8-4-2, 从可变类型派生

8-5, 多重继承

8-5-1, 方法解释顺序(MRO)

9, 内建函数

9-1, issubclass()

9-2, isinstance()

9-3, hasattr()、getattr()、setattr()、delattr()

9-4, super()

9-5, dir()

9-6, vars()

10, 用特殊方法定制类

10-1,__str__方法

10-2,__repr__方法

10-3, __add__方法, __iadd__方法

10-4, __sub__, __isub__方法

10-5, __mul__, __imul__方法

10-6, __truediv__, __idiv__, __floordiv__方法

10-7, __iter__方法

 10-8,__slots__方法

10-9, __getattribute__方法

11, 属性私有化

12, 包装

13, 描述符

13-1, 非数据描述符

13-2, 数据描述符

13-3, 实例属性的优先级大于非数据描述符

13-4, 属性的优先级

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值