15.面向对象编程

予早 2024-10-05 23:32:10
Categories: Tags:

面向对象编程,OOP,Object-oriented Programing,编程范式之一。

基本组成

类和对象、属性和方法(变量与函数)

class Person:
    uuid = "123456"  # 类属性

    def __init__(self, name, age, sex):  # 方法
        self.name = name  # 对象属性
        self.age = age  # 对象属性
        self.sex = sex  # 对象属性

    def walk(self, n):  # 方法
        print(self.name + "走路" + str(n) + "步")
        
    def run(self):
        print(str(self) + "跑路")

注:使用对象概念时,有广义和狭义之分,广义上,指Python一切皆对象,类、函数或方法、变量或属性皆为对象,狭义上,特指某场景下类实例化出来的独享。如在对象实例化p = Person("张三", 15, "男")中,广义上,pPerson"张三"15均为对象,狭义上,p为对象,Person为类。

属性,attribute

属性分为类属性和对象属性,对象属性为对象独有,类属性为该类所有对象共享。Python中支持类属性和对象属性的动态创建和删除,正因如此,仅仅通过类定义无法分析类或对象的全部属性。

类属性和对象属性

# 定义类
class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")
        
    def run(self):
        print(str(self) + "跑路")


# 实例化对象
a, b = Person("张三", 15, "男"), Person("洛五", 17, "女")

# 类属性方访问
print(a.uuid, b.uuid, Person.uuid)  # 123456 123456 123456
Person.uuid = "654321"
print(a.uuid, b.uuid, Person.uuid)  # 654321 654321 654321

# 类属性动态添加
Person.uuid2 = "uuid2"
print(a.uuid2, b.uuid2, Person.uuid2)  #

# 类属性动态删除
del Person.uuid2
# print(Person.uuid2)  # AttributeError: type object 'Person' has no attribute 'uuid2'.

# 对象属性访问
print(a.name, b.name)  # 张三 洛五
a.name, b.name = b.name[0], a.name[0]
print(a.name, b.name)  # 洛 张

# 对象属性动态创建,uuid为对象属性
a.uuid, b.uuid = "1", "2"
print(a.uuid, b.uuid, Person.uuid)

# 对象属性动态删除,打印类属性
del a.uuid, b.uuid
print(a.uuid, b.uuid, Person.uuid)

property

property装饰器修饰类中的方法,用于定义动态属性。

from math import pi


class Circle:

    def __init__(self):
        self.__r = 0

    @property
    def r(self):
        print("property")
        return pi

    @r.getter  # getter
    def r(self):
        print("getter")
        return self.__r

    @r.setter  # setter
    def r(self, r):
        print("setter")
        self.__r = r

    @r.deleter
    def r(self):
        print("deleter")
        self.__r = 0

    @property
    def area_property(self):
        return pi * self.r ** 2

    def area_func(self):
        return pi * self.r ** 2


c = Circle()
print(c.r)
# getter
# 0

c.r = 3
# setter

print(c.area_property)
# getter
# 28.274333882308138

print(c.area_func())
# getter
# 28.274333882308138

del c.r
# deleter

print(c.r)
# getter
# 0

注:关于attribute与property在python的区分的两种尝试性解释
attribute带有致敬,贡品,悼念等具有宗教意思和神学思想的意思,本身就是比较抽象的
Proper是正常的,独特的,适当的,弥撒,章节等,是更贴近于生活,是抽象的宗教和神学思想的具体化
可以理解为attribute是所有属性的统称,具体描述某一个属性时用property更准确,但用attribute无伤大雅。

方法,method

方法分为一般方法(对象方法)、类方法、静态方法。

一般方法

一般方法即对象方法,默认第一个参数为该类的实例化对象。习惯上使用对象.方法(除对象参数之外的参数列表)形式调用,调用形式直接指明对象参数,参数列表中无需手动传入,语法上支持类.方法(对象, 除对象参数之外的参数列表),调用形式未直接指明对象参数,需要手动传入对象,对象一般是该方法所属类的实例化对象,但允许传入任意对象。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")

    def run(s):  # 方法的第一个形参通常用于指向当前对象,习惯上命名为self,允许其他命名
        print(str(s) + "跑路")


def run(self):  # 习惯上,函数的形参一般不命名为self,允许该命名
    print(str(self) + "跑路")


a, b = Person("张三", 15, "男"), Person("洛五", 17, "女")

# 方法的调用
a.walk(520)  # 张三走路520步
Person.walk(a, 520)  # 张三走路520步
b.run()  # <__main__.Person object at 0x000001EE375447D0>跑路
Person.run(b)  # <__main__.Person object at 0x000001EE375447D0>跑路
Person.run("删库")  # 删库跑路,指定与Person类无关的"删库"对象

类方法

类方法是以装饰器classmethod修饰的方法,默认第一个参数为类。类方法用于执行与对象无关的操作,若操作与对象五无关,何必实例化再调用方法。习惯上,以类.方法(除类参数之外的参数列表)形式调用,调用形式直接指明类参数,参数列表中无需手动传入,语法上支持对象.方法(除类参数之外的参数列表),调用形式上并未指明类,但根据对象可以唯一确定类,同样无需手动传入类参数。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    @classmethod
    def change_uuid(cls, uuid):
        cls.uuid = uuid
        print(f"{cls.__name__} {uuid}")


a = Person("张三", 15, "男")
a.change_uuid("123")  # Person 123
Person.change_uuid("456")  # Person 456

静态方法

静态方法是以装饰器classmethod修饰的方法,没有固定的对象参数或者类参数,等同于嵌套在类中但与类无关的函数,习惯上,以类.方法(参数列表)形式调用,同样支持对象.方法(除类参数之外的参数列表)

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    @staticmethod
    def change_uuid(uuid):
        Person.uuid = uuid
        print(f"{Person.__name__} {uuid}")


a = Person("张三", 15, "男")
a.change_uuid("123")  # Person 123
Person.change_uuid("456")  # Person 456

绑定方法与非绑定方法

绑定方法指绑定到对象或者类的方法,绑定方法参数列表第一个即为绑定的对象或者类,使用对象或者类调用方法可以自动将对象或者类传入方法,特别地,对象调用类方法会传入对象对应的类。类中一般方法都是绑定到对象的方法,可以使用classmethod装饰器将方法绑定到类,可以使用staticmethod将方法解除绑定,既不绑定对象也不绑定类。

class Person:
    uuid = "123456"

    def __init__(self, name, age, sex):  # 方法
        self.name = name
        self.age = age
        self.sex = sex

    def walk(self, n):
        print(self.name + "走路" + str(n) + "步")

    @classmethod
    def change_uuid1(cls, uuid):
        cls.uuid = uuid
        print(f"{cls.__name__} {uuid}")

    @staticmethod
    def change_uuid2(uuid):
        Person.uuid = uuid
        print(f"{Person.__name__} {uuid}")


def run(self):
    print(str(self) + "跑路")


a = Person("张三", 15, "男")
print(run)  # <function run at 0x000001F4806DDEE0>
print(a.walk)  # <bound method Person.walk of <__main__.Person object at 0x000001F4806F4C50>>
print(Person.change_uuid1)  # <bound method Person.change_uuid1 of <class '__main__.Person'>>
print(Person.change_uuid2)  # <function Person.change_uuid2 at 0x000001F4806DEB60>

访问权限控制

访问权限控制决定外部可以访问属性或方法与否,分为公有public和私有private.

class A:
    __uuid = 1

    def __init__(self, a, b):
        self.a = a
        self.__b = b

    def get_b(self):
        print(f"公有方法get_b -> {self.__b}")

    def __get_b(self):
        print(f"私有方法__get_b -> {self.__b}")


o = A("a", "b")

print(o.a)  # a
# print(o.__b)  # AttributeError: 'A' object has no attribute '__b'
print(o._A__b)  # b

o.get_b()  # 公有方法get_b -> b
# o.__get_b()  # AttributeError: 'A' object has no attribute '__get_b'. Did you mean: '_A__get_b'?
o._A__get_b()  # 私有方法__get_b -> b

o.__c = "c"
print(o.__c)  # c
# print(o._A__c)  # AttributeError: 'A' object has no attribute '_A__c'. Did you mean: '_A__b'?

o.__get_c = lambda _: "c"
print(A.__dict__)
print(o.__dict__)
o.__get_c(None)
o._A__get_c(None)  # AttributeError: 'A' object has no attribute '_A__get_c'. Did you mean: '_A__get_b'?

继承、封装、多态

继承

单继承

class A:
    def __init__(self):
        print("A __init__")
        self.func()

    def func(self):
        print("A func")


class B(A):
    def func(self):
        print("B func")


b = B()  # 显然使用继承的__init__方法,调用的自己的func
# A __init__
# B func

print(b.__dict__)
print(B.__dict__)
print(A.__dict__)

多继承

多继承经典情形,菱形继承

class A:
    def __init__(self):
        print("A __init__")

    def func(self):
        print("A func")


class B(A):
    def func(self):
        print("B func")


class C(A):
    def func(self):
        print("C func")


class D(B, C):
    pass


D().func()
# A __init__
# B func

实例化链

某个类实例化对象时,在初始化该类中的对象属性之前应当先初始化父类定义的对象属性,否则可能出现调用继承自父类的方法时,由于父类对象属性未初始化导致方法调用异常。

初始化对象时,会调用__init__初始化方法使用,在该方法初始化对象属性之前,使用super来调用父类的初始化方法方法,同理,初始化父类对象属性之前,也会先调用父类的父类的初始化方法,依据继承关系,初始化方法会逐层调用直到调用顶层父类初始化方法执行完毕,随后次一层执行完毕,同理逐层执行完毕返回,直到再次回到这个类的初始化方法并执行完毕,此时整个初始化过程结束。

实例化链在C++Java都有所体现,不同是的,Python中允许中断初始化链。

class A:
    def __init__(self, a):
        self.a = a

    def get_a(self):
        print(self.a)


class B(A):
    def __init__(self, a, b):
        super().__init__(a)
        self.b = b


class C(A):
    def __init__(self, c):
        self.c = c


B(1, 2).get_a()  # 1
C(1).get_a()  # AttributeError: 'C' object has no attribute 'a'

方法解析顺序,mro

Python中使用C3算法确定方法解析顺序,访问类的__mro__属性可以获得线性序列。

class O:

    def func(self):
        s = "O"
        print(s + "()")
        return s


class A(O):
    def func(self):
        s = "A"
        print(s + "(" + super().func() + ")" + "  (注意此处,D非A父类)")
        return s


class B(O):
    def func(self):
        s = "B"
        print(s + "(" + super().func() + ")")
        return s


class C(A):
    def func(self):
        s = "C"
        print(s + "(" + super().func() + ")")
        return s


class D(B):
    def func(self):
        s = "D"
        print(s + "(" + super().func() + ")")
        return s


class E(C, D):
    def func(self):
        s = "E"
        print(s + "(" + super().func() + ")")
        return s


e = E()
e.func()
# O()
# B(O)
# D(B)
# A(D)  (注意此处,D非A父类)
# C(A)
# E(C)
print(E.__mro__)
# (<class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.O'>, <class 'object'>)
print(E.__base__)
# <class '__main__.C'>
print(E.__bases__)
# (<class '__main__.C'>, <class '__main__.D'>)


print(issubclass(A, O))  # True  是否子类
print(issubclass(A, D))  # False
print(issubclass(E, A))  # True
print(isinstance(e, E))  # True  是否实例
print(isinstance(e, C))  # True

# https://zhuanlan.zhihu.com/p/151856162
# https://zh.wikipedia.org/wiki/C3%E7%BA%BF%E6%80%A7%E5%8C%96

封装

多态

python没有向上转型,没有重载,不过调用方法时还是要根据mro搜索实际调用的方法。

抽象类

from abc import abstractmethod, ABCMeta


# Abstract Base Class
class A(metaclass=ABCMeta):
    @abstractmethod
    def func_1(self):
        pass


class A_1(A):
    def func_1(self):
        pass


a = A_1()

# 不使用ABCMeta而单独使用abstractmethods

object

顶层父类,Python3中所有类默认继承object,Python2中可选择性继承object,不继承object的类称之为经典类,继承object的类称之为新式类,Python3中一切类均为新式类。

class object:
    """
    The base class of the class hierarchy.
    
    When called, it accepts no arguments and returns a new featureless
    instance that has no instance attributes and cannot be given any.
    """
    def __delattr__(self, *args, **kwargs): # real signature unknown
        """ Implement delattr(self, name). """
        pass

    def __dir__(self, *args, **kwargs): # real signature unknown
        """ Default dir() implementation. """
        pass

    def __eq__(self, *args, **kwargs): # real signature unknown
        """ Return self==value. """
        pass

    def __format__(self, *args, **kwargs): # real signature unknown
        """ Default object formatter. """
        pass

    def __getattribute__(self, *args, **kwargs): # real signature unknown
        """ Return getattr(self, name). """
        pass

    def __getstate__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __ge__(self, *args, **kwargs): # real signature unknown
        """ Return self>=value. """
        pass

    def __gt__(self, *args, **kwargs): # real signature unknown
        """ Return self>value. """
        pass

    def __hash__(self, *args, **kwargs): # real signature unknown
        """ Return hash(self). """
        pass

    def __init_subclass__(self, *args, **kwargs): # real signature unknown
        """
        This method is called when a class is subclassed.
        
        The default implementation does nothing. It may be
        overridden to extend subclasses.
        """
        pass

    def __init__(self): # known special case of object.__init__
        """ Initialize self.  See help(type(self)) for accurate signature. """
        pass

    def __le__(self, *args, **kwargs): # real signature unknown
        """ Return self<=value. """
        pass

    def __lt__(self, *args, **kwargs): # real signature unknown
        """ Return self<value. """
        pass

    @staticmethod # known case of __new__
    def __new__(cls, *more): # known special case of object.__new__
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    def __ne__(self, *args, **kwargs): # real signature unknown
        """ Return self!=value. """
        pass

    def __reduce_ex__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __reduce__(self, *args, **kwargs): # real signature unknown
        """ Helper for pickle. """
        pass

    def __repr__(self, *args, **kwargs): # real signature unknown
        """ Return repr(self). """
        pass

    def __setattr__(self, *args, **kwargs): # real signature unknown
        """ Implement setattr(self, name, value). """
        pass

    def __sizeof__(self, *args, **kwargs): # real signature unknown
        """ Size of object in memory, in bytes. """
        pass

    def __str__(self, *args, **kwargs): # real signature unknown
        """ Return str(self). """
        pass

    @classmethod # known case
    def __subclasshook__(cls, subclass): # known special case of object.__subclasshook__
        """
        Abstract classes can override this to customize issubclass().
        
        This is invoked early on by abc.ABCMeta.__subclasscheck__().
        It should return True, False or NotImplemented.  If it returns
        NotImplemented, the normal algorithm is used.  Otherwise, it
        overrides the normal algorithm (and the outcome is cached).
        """
        pass

    __class__ = None # (!) forward: type, real value is "<class 'type'>"
    __dict__ = {}
    __doc__ = ''
    __module__ = ''

魔术方法,magic method

魔术方法指内置的名称中带有上下划线前后缀的方法,又称双下方法,例如__init__

__new__,构造方法,用于通过类创建实例

__init__,初始化方法,实例被创建后调用

__del__,终结方法,使用del将对象引用减一,引用计数为0时终结方法会被调用

class A:
    def __init__(self, a):
        print("__init__")
        self.a = a

    def __new__(cls, *args, **kwargs):
        print("__new__")
        return super().__new__(cls)

    def __del__(self):
        print("__del__")


o = A(1)
print(o.a)
del o

# __new__
# __init__
# 1
# __del__

__repr__,representation,返回一个对象的正式字符串表示,通常是便于解释器理解的,在适当环境下可以基于该字符串重建对象,若不可则返回一个 <...some useful description...>结构的表示信息。在调用内置函数repr时调用。

__str__,string,返回一个对象非正式的字符串表示,通常是便于人理解的。在调用内置函数strprintformat时调用,若没有__str__实现,会调用__repr__

s = "yuzao"
print(str(s))  # yuzao
print(repr(s))  # 'yuzao'
print(eval(repr(s)))  # yuzao eval重建对象

d = [1, '2', [3]]

print(str(d))  # [1, '2', [3]]
print(repr(d))  # [1, '2', [3]]


class C:
    def __init__(self, c):
        self.c = c


c = C(1)
print(repr(c))  # <__main__.C object at 0x000001DB21E33C50>

print(repr("12\t12\n12"))  # '12\t12\n12'
print("12\t12\n12")
# 12	12
#12
class A:

    def __str__(self):
        return "__str__"

    def __repr__(self):
        return "__repr__"


print("%s" % A())  # __str__
print("%r" % A())  # __repr__

__bytes__,返回一个bytes对象。

__format__,返回一个对象的格式化字符串,在调用内置函数format、f-string和str.format()方法时调用。

比较方法

使用关系运算符比较两个对象时会调用对应方法。

__lt__,小于

__le__,小于等于

__eq__,等于

__ne__,不等于

__gt__,大于

__ge__,大于等于

__getattribute__获取属性、__getattr__获取属性、__setattr__设置属性、__delattr__删除属性

四者为obj.key形式的操作提供底层实现

class A:

    def __getattribute__(self, item):
        print("__getattribute__")
        return super().__getattribute__(item)

    def __getattr__(self, item):
        print("__getattr__")
        return super().__getattr__(item)

    def __setattr__(self, key, value):
        print("__setattr__")
        super().__setattr__(key, value)

    def __delattr__(self, item):
        print("__delattr__")
        super().__delattr__(item)


f = A()
# f.name
f.name = '张三'  # __setattr__
f.name  # __getattribute__
del f.name  # __delattr__

__getitem__获取元素、__setitem__设置元素、__delitem__删除元素

三者为obj[key]形式的操作提供底层实现

# __getitem__ __setitem__ __delitem__


class A(dict):

    def __getitem__(self, item):
        print("__getitem__")
        return super().__getitem__(item)

    def __setitem__(self, key, value):
        print("__setitem__")
        super().__setitem__(key, value)

    def __delitem__(self, key):
        print("__delitem__")
        super().__delitem__(key)


a = A()

a['name']  # __getitem__
a['name'] = '李四'  # __setitem__
del a['name']  # __delitem__

__eq____hash__

class A:
    def __init__(self, c):
        self.c = c

    def __eq__(self, other):
        print("__eq__")
        return self.c == other.c

    def __hash__(self):
        print("__hash__")
        return self.c.__hash__()


a1, a2 = A(1), A(1)

print(a1 == a2)  # __eq__ True
print(a1 is a2, id(a1) == id(a2))  # False False
print(a1.__eq__(a2))  # __eq__ True
print(hash(a1) == hash(a2))  # __hash__ __hash__ True

__call__callable

class A:
    def __init__(self):
        self.name = "A"

    def func(self):
        print("func")


a = A()
print(hasattr(a.name, "__call__"), callable(a.name))  # False False
print(hasattr(a.func, "__call__"), callable(a.func))  # True True
print(callable(a.name), callable(A))  # False True

__dict__

一个用于存储对象的(可写)属性的字典或其他映射对象。 并非所有实例都具有 __dict__ 属性。

https://stackoverflow.com/questions/19907442/explain-dict-attribute

https://docs.python.org/zh-cn/3.13/reference/datamodel.html#object.__dict__

协议,protocol

PEP 544 – Protocols: Structural subtyping (static duck typing)

协议用于描述静态鸭子类型,约定有具有双下方法的类型即为同一类型。与Java中接口的概念类似。

迭代器协议

实现__iter____next__方法的类型即为迭代器。

上下文管理器协议

上下文管理协议,Context Management Protocol

上下文管理器,Context Manager

上下文管理器是实现上下文管理协议的类。

上下文管理器要同时实现__enter____exit__

上下文管理器执行流程、with语句

with <experssion> [as var]:
    <code_body>

其中expression是返回值为上下文管理器对象的表达式,var为一个变量,code_body为任意代码,整个执行流程如下:

  1. 执行expression返回一个上下文管理器对象
  2. 调用上下文管理器的__enter__方法,若使用as字句,将__enter__方法的返回值赋值给var
  3. 执行other_code
  4. 无论执行过程中是否发生了异常,调用上下文管理器的__exit__方法
  5. __exit__通常用于释放资源等操作,若执行过程中没有出现异常,或者语句体中执行了语句break/continue/return,则以None作为参数调用__exit__(None, None, None),若执行过程中出现异常,则使用sys.exc_info得到的异常信息为参数调用__exit__(exc_type, exc_value, exc_traceback)
  6. __exit__的返回值逻辑判断为True表示忽略可能的异常,若返回值逻辑判断为False,表示不忽略with语句code_body中可能出现的异常,会向外抛出,由外部代码处理异常,若没有发生异常且返回值逻辑判断是False,则异常抛无可抛代码会正常执行
  7. __exit__的返回值不一定必须是TrueFalse之一,若不显式指定__exit__返回值则默认返回None,逻辑判断为False,会尝试抛出可能的异常
  8. 通常使用with语句来建立并自动维护上下文,也可通过手动调用__enter____exit__的方式手动建立和维护上下文

一般形式上下文管理器

class DBManager(object):
    def __init__(self):
        print('__init__')

    def __enter__(self):
        print('__enter__')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__')
        return False


with DBManager() as db_manager:
    print(db_manager)
    print('exec code')

基于装饰器的上下文管理器

将一个生成器函数转换为一个上下文管理器

import contextlib
import time


@contextlib.contextmanager
def timeit(x):
    print(x)
    print('__enter__')
    start = time.time()
    yield
    print('__exit__')
    end = time.time()
    used_time = (end - start) * 1000
    print('Use time %d ms' % used_time)


with timeit("A"):
    time.sleep(1)

with timeit("B"):
    time.sleep(2)

属性描述符

属性描述符,descriptor,属性描述符是实现了描述符协议的类,属性描述符至少实现一个描述符协议中的方法,使用方式是将描述符实例作为另一个类的类属性,将描述符实例作为另一个类的实例属性只会被当作一般类使用。

本质上,属性描述符用于将一个普通的类属性交由一个属性描述符类来控制,可以实现复杂的属性控制逻辑。

描述符协议

描述符分类

数据描述符,data descriptor,__get____set__都定义的描述符

非数据描述符,non-data descriptor,定义__get__但不定义__set__的描述符

描述符原理

a.b会调用__getattrbute__获取属性

  1. 若有同名数据描述符,调用__get__返回
  2. __dict__中有同名属性,返回
  3. 若有同名非数据描述符,调用__get__返回
  4. 若a所属的类有__getattr__属性,调用返回
  5. 都没有抛出AttributeError
import uuid


class Identity:
    def __init__(self):
        self.value = uuid.uuid4()

    def __get__(self, instance, owner):
        print(f"call __get__ {instance=}, {owner=}")
        return self.value


class Age:
    def __init__(self, value=0):
        self.value = value

    def __get__(self, instance, owner):
        print(f"call __get__ {instance=}, {owner=}")
        return self.value

    def __set__(self, instance, value):
        if value < 0:
            raise ValueError("年龄必须为非负整数")
        print(f"call __set__ {instance=}, {value=}")
        self.value = value


class Person:
    pid = Identity()
    age = Age()

    def __init__(self, name, age):
        self.name = name
        self.age = age


person1 = Person('张三', 18)  # call __set__ instance=<__main__.Person object at 0x000001DE1E1D1E90>, value=18
person1.age  # call __get__ instance=<__main__.Person object at 0x000001DE1E1D1E90>, owner=<class '__main__.Person'>
Person.age  # call __get__ instance=None, owner=<class '__main__.Person'>
# person1.age = -1  # ValueError: 年龄必须为非负整数


person2 = Person('李四', 20)
print(person1.name, person1.age)  # 张三 20

# 属性描述符实例age作为类属性会被所有实例共享
# 而本场景下所有Person的实例的age应当是不一样的
# 当前代码中还需要实现所有Person实例从同一个age属性描述符实例中获取到自己的age

描述符示例

class Person:
    def __init__(self):
        self.name = "A"

    def name(self):
        return "B"


print(Person().name)  # A
print(dir(Person.name)) # [..., __get__, ...]

代码示例

仿照SQLAlchemy

from abc import abstractmethod


class Value:

    def __init__(self):
        self.data = {}

    def __get__(self, instance, owner):
        return self.data[instance]  # 区分实例的属性

    def __set__(self, instance, value):
        self.validate(value)
        self.data[instance] = value

    @abstractmethod
    def validate(self, value):
        pass


class Number(Value):

    def __init__(self, min_value=None, max_value=None):
        super(Number, self).__init__()
        self.minvalue = min_value
        self.maxvalue = max_value

    def validate(self, value):
        if not isinstance(value, (int, float)):
            raise TypeError(f'Expected {value!r} to be an int or float value')
        if value < self.minvalue:
            raise ValueError(f'Expected {value!r} not to be less than {self.minvalue!r}')
        if value > self.maxvalue:
            raise ValueError(f'Expected {value!r} not to be greater than {self.maxvalue!r}')


class String(Value):

    def __init__(self, min_size=None, max_size=None):
        super(String, self).__init__()
        self.minsize = min_size
        self.maxsize = max_size

    def validate(self, value):
        if not isinstance(value, str):
            raise TypeError(f'Expected {value!r} to be a str')
        if len(value) < self.minsize:
            raise ValueError(f'Expected length of {value!r} not to be less than {self.minsize!r}')
        if len(value) > self.maxsize:
            raise ValueError(f'Expected length of {value!r} not to be grater than {self.maxsize!r}')


class Person:
    name = String(min_size=1, max_size=10)
    age = Number(min_value=1, max_value=120)

    def __init__(self, name, age):
        self.name = name
        self.age = age


person1 = Person('张三', 18)
print(person1.name, person1.age)  # 张三 18

person2 = Person('李四', 20)
print(person2.name, person2.age)  # 李四 20

person3 = Person('王五', 121)  # ValueError: Expected 121 not to be greater than 120

元类

https://blog.csdn.net/a2011480169/article/details/87891753

元类用于生成类对象,类本身是一种对象,而元类就是这种对象对应的类,Python 中继承type的都是元类,可以继承type来自定义元类。

元类可以控制类的创建和类的实例化行为,是通过__call____new____init__实现的。

元类对类创建的控制:类作为元类实例化的对象,类的创建必然要调用元类的__new__、和__init__方法,这样类创建可以在元类中控制。

元类对类的实例化的控制:若元类为A、类为B,则有B = A(),那么类的实例化就是A()(),这是就会调用元类的__call__方法,而元类的__call__方法,会负责调用传进来的类的__new____init__方法完成实例化,倘若改写元类__call__方法,则可以控制类实例化行为,例如实现单例模式。