# python元编程

***

## 动态属性

在 Python 中，数据的属性和处理数据的方法统称**属性**（attribute）。其实，方法只是**可调用的**属性。

## 属性描述符(get/set/delete)

> [python 使用特性管理实例属性](https://halfclock.github.io/2019/06/01/python-property/)
>
> [(转)Python描述符（descriptor）解密](https://lingxiankong.github.io/2014-03-28-python-descriptor.html)
>
> [python理解描述符(descriptor)](https://www.lagou.com/lgeduarticle/9290.html)
>
> [python 描述符总结](https://www.jianshu.com/p/fe66aebc02ec)
>
> [Python描述符 (descriptor) 详解](https://www.cnblogs.com/Jimmy1988/p/6808237.html)
>
> [在不可散列的类中使用描述符-python](https://www.coder.work/article/1262710)
>
> [python高级编程——描述符Descriptor详解（下篇）](https://blog.csdn.net/qq_27825451/article/details/84848341?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase\&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase)

### 基本概念

描述符是对多个属性运用相同存取逻辑的一种方式。例如，Django ORM 和 SQL Alchemy 等 ORM 中的字段类型是描述符，把数据库记录中字段里的数据与 Python 对象的属性对应起来。

> **为什么需要描述符**：对property来说，最大的缺点就是它们不能重复使用。虽然property可以让类从外部看起来接口整洁漂亮，但是却做不到内部同样整洁漂亮。
>
> 描述符是property的升级版，允许你为重复的property逻辑编写单独的类来处理。
>
> **基本要求**：描述符是实现了特定协议的类，这个协议包括 `__get__`、`__set__` 和 `__delete__` 方法。`property` 类实现了完整的描述符协议。通常，可以只实现部分协议。其实，我们在真实的代码中见到的大多数描述符只实现了 `__get__` 和 `__set__` 方法，还有很多只实现了其中的一个。
>
> 实现了 `__get__`、`__set__` 或 `__delete__` 方法的类是描述符。
>
> **用法**：描述符的用法是，创建一个描述符类，它的实例对象作为另一个类的属性。
>
> 为了让描述符能够正常工作，它们必须定义在类的层次上。如果你不这么做，那么Python无法自动为你调用`__get__`和`__set__`方法。
>
> **大致流程**：
>
> 1. 定义一个描述符类D，其内包含一个或多个`__get__()`、`__set__()`、`__delete__()`方法
> 2. 将描述符类D的实例对象d赋值给另一个要代理的类中某个属性attr，即attr=D()
> 3. 之后访问、赋值、删除attr属性，将会自动触发描述符类中的`__get__()`、`__set__()`、`__delete__()`方法
>
> **实现**：
>
> 要定义描述符类很简单，只要某个类中包含了下面一个或多个方法，就算是满足描述符协议，就是描述符类，就可以作为属性操作的代理器。
>
> ```python
> class Descriptor():
>     def __get__(self, instance, owner):...
>     def __set__(self, instance, value):...
>     def __delete__(self, instance):...
> ```
>
> 需要注意的是，`__get__`的返回值需要是属性值或抛异常，另外两个方法要返回None。
>
> **类属性描述符对象和实例属性同名时**：描述符针对的是类属性，但是当一个类中，如果类属性是描述符对象，而实例属性由于这个描述符属性同名
>
> ```python
> class Person:
>     character = CharacterDescriptor('乐观的')
>     weight = WeightDescriptor(150)
>     def __init__(self, character,weight):
>         self.character = character
>         self.weight = weight
>        
> p = Person('悲观的', 200)
> print(p.character)  #属性的访问
> print(p.weight)     #
> ```
>
> 从上面的运行结果可以看出，首先是访问了描述符的\_\_set\_\_方法，这是因为在构建对象的时候，相当于为character和weight赋值，然后再调用\_\_get\_\_方法，这是因为访问了类属性character和weight，但是最终打印出来值却并不是类属性的值，这是因为，实例属性实际上是在“描述符类属性”后面访问的，所以覆盖掉了。

### 专有名词

> **描述符类**: **实现了描述符协议的类**，描述符类的一些协议(`__get__`、`__set__`或`__delete__` )。
>
> 实现了`__get__`、`__set__`、`__delete__` 方法的类是描述符，只要实现了其中一个就是。
>
> **托管类**： **将描述符实例作为类属性的类**，比如Fruits 类，他有 weight、price 两个类属性，且都被赋予了描述符类的实例。
>
> **描述符实例**: 描述符类创建出描述符实例，**通常来讲，描述符类的实例会被赋给托管类的类属性。**
>
> **托管实例**: 托管类创建出来的实例
>
> **托管属性**: 托管类中由描述符实例处理的公开属性
>
> **存储属性**: 可以粗略的理解为、托管实例的属性、在上例中使用 `vars(apple)` 得到的结果中 price 和 weight **实例属性**就是存储属性，它们实际**存储着***实例的***属性值**
>
> **非数据描述符**：一个类，如果只定义了\_\_get\_\_() 或者是\_\_delete\_\_()方法，而没有定义\_\_set\_\_()方法，则认为是非数据描述符(即没有定义\_\_set\_\_)
>
> **数据描述符**：一个类，不仅定义了\_\_get\_\_() 方法，还定义\_\_set\_\_(), \_\_delete\_\_() 方法，则认为是数据描述符(即定义了\_\_get\_\_和\_\_set\_\_)

> ps： **托管属性**是类(Fruits)属性、存储属性是实例(apple)的属性。

![img](https://pic.hycbook.com/i/hexo/bk_resources/python/7.python%E5%85%83%E7%BC%96%E7%A8%8B/image-20200625193223844.webp)>

> `Quantity` 实例是描述符，因此有个放大镜，用于获取值（`__get__`），以及一个手抓，用于设置值（`__set__`）。

### 例子

#### 要点

> **定义位置**：为了让描述符能够正常工作，它们必须定义在类的层次上。如果你不这么做，那么Python无法自动为你调用\_\_get\_\_和\_\_set\_\_方法。
>
> **独立实例**：类使用了一个字典来单独保存专属于实例的数据。这个一般来说是没问题的，除非你用到了不可哈希（unhashable）的对象
>
> **不可哈希处理**：list的子类是不可哈希的，因此它们不能为描述符类用做数据字典的key。有一些方法可以规避这个问题，但是都不完美。最好的方法可能就是给你的描述符加标签了。描述符可以安全的在这里存储数据。只是要记住，不要在别的地方也给这个描述符添加标签。这样的代码很脆弱也有很多微妙之处。但这个方法的确很普遍，可以用在不可哈希的所有者类上。
>
> ```python
> class Descriptor(object):
>     def __init__(self, label):
>         self.label = label
>
>     def __get__(self, instance, owner):
>         print('__get__', instance, owner)
>         return instance.__dict__.get(self.label)
>
>     def __set__(self, instance, value):
>         print('__set__')
>         instance.__dict__[self.label] = value
>
> class Foo(list):
>     x = Descriptor('x')
>     y = Descriptor('y')
> ```
>
> **泄漏内存问题**：WeakKeyDictionary可以保证描述符类不会泄漏内存：WeakKeyDictionary的特殊之处在于：如果运行期系统发现这种字典所持有的引用，是整个程序里面指向Exam实例的最后一份引用，那么，系统就会自动将该实例从字典的键中移除。

#### 模拟ORM

代码-描述符类

```python
#!/usr/bin/env Python
# -- coding: utf-8 --
import weakref
import numbers

class Field:
    pass

class IntField(Field):
    # 数据描述符
    def __init__(self, db_column, min_value=None, max_value=None):
        self.min_value = min_value
        self.max_value = max_value
        self.db_column = db_column
        if min_value is not None:
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("min_value must be int")
            elif min_value < 0:
                raise ValueError("min_value must be positive int")
        if max_value is not None:
            if not isinstance(max_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif max_value < 0:
                raise ValueError("max_value must be positive int")
        if min_value is not None and max_value is not None:
            if min_value > max_value:
                raise ValueError("min_value must be smaller than max_value")
        self._value = weakref.WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._value.get(instance, 0)

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < self.min_value or value > self.max_value:
            raise ValueError("value must between min_value and max_value")
        self._value[instance] = value


class CharField(Field):
    def __init__(self, db_column, max_length=None):
        # self._value = None
        self.db_column = db_column
        if max_length is None:
            raise ValueError("you must spcify max_lenth for charfiled")
        self.max_length = max_length
        self._value = weakref.WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._value.get(instance, '0')

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("string value need")
        if len(value) > self.max_length:
            raise ValueError("value len excess len of max_length")
        self._value[instance] = value
```

代码-元类

```python
class ModelMetaClass(type):
    def __new__(cls, name, bases, attrs, **kwargs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs, **kwargs)
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        attrs_meta = attrs.get("Meta", None)
        _meta = {}
        db_table = name.lower()
        if attrs_meta is not None:
            table = getattr(attrs_meta, "db_table", None)
            if table is not None:
                db_table = table
        _meta["db_table"] = db_table
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        del attrs["Meta"]
        return super().__new__(cls, name, bases, attrs, **kwargs)


class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        return super(BaseModel, self).__init__()

    def save(self):
        fields = []
        values = []
        for key, value in self.fields.items():
            db_column = value.db_column
            if db_column is None:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))

        sql = "insert {db_table}({fields}) value({values})".format(db_table=self._meta["db_table"],
                                                                   fields=",".join(fields), values=",".join(values))
        print(sql)
```

代码-测试

```
class User(BaseModel):
    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=1, max_value=100)
    class Meta:
        db_table = "user"

if __name__ == '__main__':
    first_user = User(name="bobby", age=28)
    first_user.name = "bobby"
    first_user.age = 28
    second_user = User(name="bobby", age=23)
    print(first_user.name is second_user.name)
    second_user.name = 'okay'
    print(first_user.name is second_user.name)

    second_user.name = 'sec_boddy'
    print(first_user.name)
    print(second_user.name)
    print(first_user.name is second_user.name)
```

输出

```cmd
True
False
bobby
sec_boddy
False
```

#### 成绩管理

代码

```python
#!/usr/bin/env Python
# -- coding: utf-8 --
import weakref

class Grade:
    def __init__(self):
        self._values = weakref.WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._values.get(instance, 0)

    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError('Grade must be between 0 and 100')
        self._values[instance] = value

class Exam:
    # https://lingxiankong.github.io/2014-03-28-python-descriptor.html
    # 为了让描述符能够正常工作，它们必须定义在类的层次上。如果你不这么做，那么Python无法自动为你调用__get__和__set__方法。
    # 确保实例的数据只属于实例本身
    math_grade = Grade()
    writing_grade = Grade()
    science_grade = Grade()
    
if __name__ == '__main__':
    first_exam = Exam()
    first_exam.writing_grade = 82
    second_exam = Exam()
    second_exam.writing_grade = 75
    logger.info(f'first {first_exam.writing_grade}')
    logger.info(f'second {second_exam.writing_grade}')    
```

输出

```cmd
2020-06-25 20:18:23 lazy_db INFO:  first 82
2020-06-25 20:18:23 lazy_db INFO:  second 75
```

#### 成绩管理2.0

代码

```python
class Grade:
    def __init__(self, name):
        self.name = name
        self.internal_name = '_' + self.name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.internal_name, '')

    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError('Grade must be between 0 and 100')
        setattr(instance, self.internal_name, value)

class Exam:
    # https://lingxiankong.github.io/2014-03-28-python-descriptor.html
    # 为了让描述符能够正常工作，它们必须定义在类的层次上。如果你不这么做，那么Python无法自动为你调用__get__和__set__方法。
    # 确保实例的数据只属于实例本身
    math_grade = Grade('math_grade')
    writing_grade = Grade('writing_grade')
    science_grade = Grade('science_grade')

if __name__ == '__main__':
    first_exam = Exam()
    first_exam.writing_grade = 82
    second_exam = Exam()
    second_exam.writing_grade = 75
    logger.info(f'first {first_exam.writing_grade}')
    logger.info(f'second {second_exam.writing_grade}')
```

输出

```cmd
2020-06-25 20:18:23 lazy_db INFO:  first 82
2020-06-25 20:18:23 lazy_db INFO:  second 75
```

#### 成绩管理2.1

代码

```python
class Grade:
    def __init__(self):
        self.name = None
        self.internal_name = None

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.internal_name, '')

    def __set__(self, instance, value):
        if not (0 <= value <= 100):
            raise ValueError('Grade must be between 0 and 100')
        setattr(instance, self.internal_name, value)

class Meta(type):
    def __new__(cls, name, bases, class_dict):
        for key, value in class_dict.items():
            if isinstance(value, Grade):
                value.name = key
                value.internal_name = '_' + key
        cls = type.__new__(cls, name, bases, class_dict)
        return cls

class Exam(object,metaclass=Meta):
    # https://lingxiankong.github.io/2014-03-28-python-descriptor.html
    # 为了让描述符能够正常工作，它们必须定义在类的层次上。如果你不这么做，那么Python无法自动为你调用__get__和__set__方法。
    # 确保实例的数据只属于实例本身
    math_grade = Grade()
    writing_grade = Grade()
    science_grade = Grade()

    def __init__(self, writing_grade):
        self.writing_grade = writing_grade

if __name__ == '__main__':
    first_exam = Exam(85)
    first_exam.writing_grade = 82
    second_exam = Exam(13)
    second_exam.writing_grade = 75
    logger.info(f'first {first_exam.writing_grade}')
    logger.info(f'second {second_exam.writing_grade}')
```

### 进阶

#### 添加回调

> 描述符仅仅是类，也许你想要为它们增加一些方法。举个例子，描述符是一个用来回调property的很好的手段。比如我们想要一个类的某个部分的状态发生变化时就立刻通知我们。下面的大部分代码是用来做这个的：

```python
class CallbackProperty(object):
    """A property that will alert observers when upon updates"""
    def __init__(self, default=None):
        self.data = WeakKeyDictionary()
        self.default = default
        self.callbacks = WeakKeyDictionary()

    def __get__(self, instance, owner):
        return self.data.get(instance, self.default)

    def __set__(self, instance, value):        
        for callback in self.callbacks.get(instance, []):
            # alert callback function of new value
            callback(value)
        self.data[instance] = value

    def add_callback(self, instance, callback):
        """Add a new function to call everytime the descriptor updates"""
        #but how do we get here?!?!
        if instance not in self.callbacks:
            self.callbacks[instance] = []
        self.callbacks[instance].append(callback)

class BankAccount(object):
    balance = CallbackProperty(0)

def low_balance_warning(value):
    if value < 100:
        print "You are poor"

ba = BankAccount()

# will not work -- try it
#ba.balance.add_callback(ba, low_balance_warning)
```

> 这是一个很有吸引力的模式——我们可以自定义回调函数用来响应一个类中的状态变化，而且完全无需修改这个类的代码。这样做可真是替人分忧解难呀。现在，我们所要做的就是调用ba.balance.add\_callback(ba, low\_balance\_warning)，以使得每次balance变化时low\_balance\_warning都会被调用。
>
> 但是我们是如何做到的呢？当我们试图访问它们时，描述符总是会调用`__get__`。就好像add\_callback方法是无法触及的一样！其实关键在于利用了一种特殊的情况，即，当从类的层次访问时，`__get__`方法的第一个参数是None。

```python
class CallbackProperty(object):
    """A property that will alert observers when upon updates"""
    def __init__(self, default=None):
        self.data = WeakKeyDictionary()
        self.default = default
        self.callbacks = WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self        
        return self.data.get(instance, self.default)

    def __set__(self, instance, value):
        for callback in self.callbacks.get(instance, []):
            # alert callback function of new value
            callback(value)
        self.data[instance] = value

    def add_callback(self, instance, callback):
        """Add a new function to call everytime the descriptor within instance updates"""
        if instance not in self.callbacks:
            self.callbacks[instance] = []
        self.callbacks[instance].append(callback)

class BankAccount(object):
    balance = CallbackProperty(0)

def low_balance_warning(value):
    if value < 100:
        print "You are now poor"

ba = BankAccount()
BankAccount.balance.add_callback(ba, low_balance_warning)

ba.balance = 5000
print "Balance is %s" % ba.balance
ba.balance = 99
print "Balance is %s" % ba.balance
Balance is 5000
You are now poor
Balance is 99
```

#### 实现底层 @classmethod

```python

class NewDefine_classmethod:
    """
    使用“描述符”和“装饰器”结合起来，模拟@classmethod
    """
    def __init__(self, function):
        self.function = function
 
    def __get__(self, instance, owner):
        #对传进函数进行加工,最后返回该函数
        def wrapper(*args, **kwargs):   #使用不定参数是为了匹配需要修饰的函数参数
            print("给函数添加额外功能")
            self.function(owner, *args, **kwargs)
        return wrapper
 
class Person:
    name='我有姓名'
    def __init__(self):
        pass
 
    @NewDefine_classmethod
    def study_1(cls):
        print(f'我的名字是：{cls.name},我会搞学习！')
 
    @NewDefine_classmethod
    def study_2(cls,score):
        print(f'我的名字是：{cls.name},我会搞学习！,而且这次考试考了 {score} 分')
    
print(Person.study_1())
print(Person.study_2(99))
```

可以分这样几步分析：

> 第一步：@NewDefine\_classmethod本质上是一个“类装饰器”，从它的定义可知，它的定义为
>
> class NewDefine\_classmethod(function).我们发现，python系统定义的@classmethod其实它的定义也是一样的，如下，
>
> class classmethod(function) .怎么样？它们二者的定义是不是一样？
>
> 第二步：NewDefine\_classmethod本质上又是一个描述符，因为在它的内部实现了\_\_get\_\_协议，由此可见，NewDefine\_classmethod是“集装饰器-描述符”于一身的。
>
> 第三步：运行过程分析，因为study\_1=NewDefine\_classmethod（study\_1）,所以，study\_1本质上是一个NewDefine\_classmethod的对象，又因为NewDefine\_classmethod本质上是实现了描述符的，所以，study\_1本质上是一个定义在类中的描述符属性。
>
> 第四步：因为study\_1本质上是一个定义在类中的描述符属性。所以在执行Person.study\_1的时候，相当于是访问类的描述符属性，所以会进入到描述符的\_\_get\_\_方法。
>
> 现在是不是觉得原来python描述符还有这样神奇的使用呢？
>
> 注意：如果修饰的函数本身是具有返回值的，在\_\_get\_\_里面所定义的wrapper里面一定要返回，即return self.function(owner, \*args, \*\*kwargs)。
>
> 还有一个地方需要注意的是，因为这是自定义的底层实现，所以一些集成IDE可能会显示有语法错误，但是这没有关系，这正是python灵活多变的地方，运行并不会出现错误。

#### 实现底层 @staticmethod

staticmethod方法与classmethod方法的区别在于classmethod方法在使用需要传进一个类的引用作为参数。而staticmethod则不用。

```python
class NewDefine_staticmethod:
    """
    使用“描述符”和“装饰器”结合起来，模拟@classmethod
    """
    def __init__(self, function):
        self.function = function
 
    def __get__(self, instance, owner):
        #对传进函数进行加工,最后返回该函数
        def wrapper(*args, **kwargs):   #使用不定参数是为了匹配需要修饰的函数参数
            print("给函数添加额外功能")
            self.function(*args, **kwargs)
        return wrapper
 
class Person:
    name='我有姓名'
    def __init__(self):
        pass
 
    @NewDefine_staticmethod
    def study_1(math,english):
        print(f'我数学考了 {math} 分,英语考了 {english} 分,我会搞学习！')
 
    @NewDefine_staticmethod
    def study_2(history,science):
        print(f'我历史考了 {history} 分,科学考了 {science} 分,我会搞学习！')
    
print(Person.study_1(99,98))
print(Person.study_2(88,89))
```

类方法classmethod必须第一个参数是cls，这个实际上就是判断所属的那个类，因此在\_\_get\_\_里面的function在调用的时候，第一个参数需要传递为owner，因为所属的“类cls等价于Person等价于owner”，但是因为静态方法不需要任何参数cls或者是self都不需要，因此在\_\_get\_\_实现的时候不能再传递owner参数，否则会显示参数错误。

#### 实现底层 @property

```python
class NewDefine_property:
    """
    使用“描述符”和“装饰器”结合起来，模拟@classmethod
    """
    def __init__(self, function):
        self.function = function
 
    def __get__(self, instance, owner):
        print("给函数添加额外功能")
        return self.function(instance)
 
class Person:
    name='我有姓名'
    def __init__(self):
        self.__study=100
 
    @NewDefine_property
    def study_1(self):  #使用property装饰的函数一般不要用“参数”，因为它的主要功能是对属性的封装
        return self.__study
 
p=Person()
print(p.study_1)
```

基本思想和前面分析的还是一样的，但是有几个地方有所区别，需要注意：

> 第一：@property的目的是封装一个方法，是这个方法可以被当做属性访问
>
> 第二：调用的方式与前面有所不同，\_\_get\_\_里面不能再定义wrapper了，否则不会调用wrapper。得不到想要的结果，为什么呢？
>
> 因为调用的方式不一样，根据前面的分析，study\_1的本质是描述符属性，但是前面的调用均是使用的
>
> Person.study\_1()或者是p.study\_1()的形式，还是当成方法去使用的。但是此处不一样了，直接就是当成属性去使用，
>
> p.study\_1 ，不再是方法调用，因此wrapper函数得不到调用。所以\_\_get\_\_方法得到了进一步简化。

## 按需生成属性

> [Python 魔法方法（三） \_\_getattr\_\_，\_\_setattr\_\_， \_\_delattr\_\_](https://blog.csdn.net/yusuiyu/article/details/87945149)
>
> [Python高级用法之动态属性](https://blog.csdn.net/biheyu828/article/details/82794156)

> 使用\_\_getattr\_\_、\_\_setattr\_\_和\_\_getattribute\_\_来动态生成属性
>
> Python 语言提供了一些挂钩，使得开发者很容易就能编写出通用的代码，以便将多个系统黏合起来。例如，我们要把数据库的行（row）表示为 Python 对象。由于数据库有自己的一套结构（schema），也称架构、模式、纲要、概要、大纲，所以在操作与行相对应的对象时，我们必须知道这个数据库的结构。然而，把 Python 对象与数据库相连接的这些代码，却不需要知道行的结构，所以，这部分代码应该写得通用一些。
>
> 那么，如何实现这种通用的代码呢？普通的实例属性、@property 方法和描述符，都不能完成此功能，因为它们都必须预先定义好，而像这样的动态行为，则可以通过 Python 的\_\_getattr\_\_特殊方法来做。如果某个类定义了\_\_getattr\_\_，同时系统在该类对象的实例字典中又找不到待查询的属性，那么，系统就会调用这个方法。

### 实例属性查找

> [python属性查找（attribute lookup）](https://www.shuzhiduo.com/A/gVdn4oWlJW/)
>
> 首先需要明白的是实例属性查找的过程：

如果obj是某个类的实例，那么obj.name（以及等价的getattr(obj,'name')）首先调用\_\_getattribute\_\_。如果类定义了\_\_getattr\_\_方法，那么在\_\_getattribute\_\_抛出 AttributeError 的时候就会调用到\_\_getattr\_\_，而对于描述符(\_\_get\_\_）的调用，则是发生在\_\_getattribute\_\_内部的。官网文档是这么描述的

> The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to [`__getattr__()`](https://docs.python.org/2/reference/datamodel.html#object.__getattr__) if provided.

obj = Clz(), 那么obj.attr 顺序如下：

> （1）如果“attr”是出现在Clz或其基类的\_\_dict\_\_中， 且attr是data descriptor， 那么调用其\_\_get\_\_方法, 否则
>
> （2）如果“attr”出现在obj的\_\_dict\_\_中， 那么直接返回 obj.\_\_dict\_\_\['attr']， 否则
>
> （3）如果“attr”出现在Clz或其基类的\_\_dict\_\_中
>
> （3.1）如果attr是non-data descriptor，那么调用其\_\_get\_\_方法， 否则
>
> （3.2）返回 \_\_dict\_\_\['attr']
>
> （4）如果Clz有\_\_getattr\_\_方法，调用\_\_getattr\_\_方法，否则
>
> （5）抛出AttributeError

> 程序每次访问对象的属性时，Python 系统都会调用这个特殊方法，即使属性字典里面已经有了该属性，也依然会触发 \_\_getattribute\_\_ 方法。这样就可以在程序每次访问属性时，检查全局事务状态。

> 按照 Python 处理缺失属性的标准流程，如果程序动态地访问了一个不应该有的属性，那么可以在 \_\_getattr\_\_ 和 \_\_getattribute\_\_ 里面抛出 AttributeError 异常。

> 实现通用的功能时，我们经常会在 Python 代码里使用内置的 hasattr 函数来判断对象是否已经拥有了相关的属性，并用内置的 \_\_getattr\_\_ 函数来获取属性值。这些函数会先在实例字典中搜索待查询的属性，然后再调用 \_\_getattr\_\_。

### 四个魔法函数

> **访问时机**
>
> 如果某个类定义了\_\_getattr\_\_ ，同时系统在该类对象的实例字典中又找不到待查询的属性，那么，系统就会调用这个方法。
>
> 程序每次访问对象的属性时，Python 系统都会调用这个特殊方法，即使属性字典里面已经有了该属性，也依然会触发\_\_getattribute\_\_方法。这样就可以在程序每次访问属性
>
> 按照Python处理缺失属性的标准流程，如果程序动态地访问了一个不应该有的属性，那么可以在\_\_getattr\_\_ 和\_\_getattribute\_\_ 里面抛出AttributeError异常。
>
> 只要对实例的属性赋值，无论是直接赋值，还是通过内置的setattr函数赋值，都会触发\_\_setattr\_\_方法 。

#### \_\_getattr\_\_

当我们访问一个不存在的属性的时候，会抛出异常，提示我们不存在这个属性。而这个异常就是\_\_getattr\_\_方法抛出的，其原因在于他是访问一个不存在的属性的最后落脚点，作为异常抛出的地方提示出错再适合不过了。

看例子，我们找一个存在的属性和不存在的属性。

```python
class A(object):
    def __init__(self, value):
        self.value = value
 
    def __getattr__(self, item):
        print "into __getattr__"
        return  "can not find"
 
a = A(10)
print a.value
# 10
print a.name
# into __getattr__
# can not find
```

可以看出，访问存在的属性时，会正常返回值，若该值不存在，则会进入最后的兜底函数\_\_getattr\_\_。

#### \_\_setattr\_\_

在对一个属性设置值的时候，会调用到这个函数，每个设置值的方式都会进入这个方法。

```PYTHON
class A(object):
    def __init__(self, value):
        print "into __init__"
        self.value = value
 
    def __setattr__(self, name, value):
        print "into __setattr__"
        if value == 10:
            print "from __init__"
        object.__setattr__(self, name, value)
 
 
a = A(10)
# into __init__
# into __setattr__
# from __init__
print a.value
# 10
a.value = 100
# into __setattr__
print a.value
# 100
```

在实例化的时候，会进行初始化，在\_\_init\_\_里，对value的属性值进行了设置，这时候会调用\_\_setattr\_\_方法。

在对a.value重新设置值100的时候，会再次进入\_\_setattr\_\_方法。

需要注意的地方是，在重写\_\_setattr\_\_方法的时候千万不要重复调用造成死循环。

```python
class A(object):
    def __init__(self, value):
        self.value = value
 
    def __setattr__(self, name, value):
        self.name = value
```

这是个死循环。当我们实例化这个类的时候，会进入\_\_init\_\_，然后对value进行设置值，设置值会进入\_\_setattr\_\_方法，而\_\_setattr\_\_方法里面又有一个self.name=value设置值的操作，会再次调用自身\_\_setattr\_\_，造成死循环。

除了上面调用object类的\_\_setattr\_\_避开死循环，还可以如下重写\_\_setattr\_\_避开循环。

```PYTHON
class A(object):
    def __init__(self, value):
        self.value = value
 
    def __setattr__(self, name, value):
        self.__dict__[name] = value
 
 
a = A(10)
print a.value
# 10
```

#### \_\_delattr\_\_

\_\_delattr\_\_是个删除属性的方法

```PYTHON
class A(object):
    def __init__(self, value):
        self.value = value
 
    def __delattr__(self, item):
        object.__delattr__(self, item)
 
    def __getattr__(self, item):
        return "when can not find attribute into __getattr__"
 
 
 
a = A(10)
print a.value
# 10
del a.value
print a.value
# when can not find attribute into __getattr__
```

\_\_delattr\_\_也要避免死循环的问题，就如\_\_setattr\_\_一样，在重写\_\_delattr\_\_，避免重复调用。

#### \_\_getattribute\_\_

> 使用\_\_getattribute\_\_对属性的访问做额外处理
>
> 假设我们需要在数据库中实现事物（transaction）处理，即每次在访问属性时，需要额外调用特殊方法检查数据库中对应的行是否有效，以及相关的事务是否依然开放。此时使用\_\_getattr\_\_无法实现这种功能，因为第二次访问属性时，Python会直接返回上首次调用时存储在\_\_dict\_\_中的属性值，而不会再次调用\_\_getattr\_\_插寻属性的状态。此种情况下我们需要使用\_\_getattribute\_\_，该方法在用户每次访问属性是都会被调用。

```python
class LazyDB(object):
    def __init__(self):
        self.exist = 1
 
    def __getattribute__(self, item):
        print('__getattribute__ (%s) called' % item)
        try:
            return super().__getattribute__(item)
        except AttributeError:
            value = ' '.join(['default value: ', item])
            setattr(self, item, value)
            return value
 
data = LazyDB()
print(data.foo) ##每次访问类属性时都会被调用，此处是第1次调用
print(data.foo) ##每次访问类属性时都会被调用，此处是第2次调用
print(data.__dict__)  ##每次访问类属性时都会被调用，此处是第3次待用
 
###输出如下：
__getattribute__ (foo) called
default value:  foo
__getattribute__ (foo) called
default value:  foo
__getattribute__ (__dict__) called
{'exist': 1, 'foo': 'default value:  foo'}
```

### 要点

> * \_\_getattr\_\_ 和 \_\_setatr\_\_，我们可以用惰性的方式来加载并保存对象的属性。
> * 要理解 \_\_getattr\_\_ 与 \_\_getattribute\_\_ 的区别：前者只会在待访问的属性缺失时触发，而后者则会在每次访问属性时触发。
> * 如果要在 \_\_getattribute\_\_ 和 \_\_setattr\_\_ 方法中访问实例属性，那么应该直接通过super()（也就是object类的同名方法）来做，以避免无限递归。
>
> 总结：
>
> （1）对于类装饰器属性，只要出现属性访问（不管是通过对象访问还是类名访问），都会优先调用装饰器的\_\_get\_\_方法;
>
> （2）对于类装饰器属性，若出现属性修改（不管是通过对象访问还是类名访问），都会优先调用装饰器的\_\_set\_\_方法;
>
> （3）对于类装饰器属性，若出现属性删除（不管是通过对象访问还是类名访问），都会优先调用装饰器的\_\_delete\_\_方法

### 例子

## 元类

> [第33/34条用元类核实和登记子类,3334,验证,注册](https://www.pythonf.cn/read/35687)

### 基本概念

> 类元编程是指在运行时创建或定制类的技艺。在 Python 中，类是一等对象，因此任何时候都可以使用函数新建类，而无需使用 `class` 关键字。类装饰器也是函数，不过能够审查、修改，甚至把被装饰的类替换成其他类。最后，元类是类元编程最高级的工具：使用元类可以创建具有某种特质的全新类种，例如我们见过的抽象基类。

#### 元类的定义

> Python定义元类时，需要从 `type` 类中继承，然后重写 `__new__` 方法，便可以实现意想不到的功能。
>
> ```python
> class Meta(type):
>     def __new__(meta,name,bases,class_dict):
>         #...各种逻辑实现1
>         cls = type.__new__(meta,name,bases,class_dict)
>         print('当前类名',name)
>         print('父类',bases)
>         print('全部类属性',class_dict)
>         #...各种逻辑实现2
>         return cls
>
> class MyClass(object,metaclass=Meta):
>     stuff = 33
>     def foo(self):
>         pass
> ```
>
> ```cmd
> 当前类名 MyClass
> 父类 (<class 'object'>,)
> 全部类属性 {'__module__': '__main__', '__qualname__': 'MyClass', 'stuff': 33, 'foo': <function MyClass.foo at 0x0000019E028315E8>}
> ```
>
> 元类可以获知那个类的名称、其所继承的父类，以及定义在class语句体中的全部类属性

#### 元类的本质

在Python当中万物皆对象，我们用 `class` 关键字定义的类本身也是一个对象， **负责产生该对象的类称之为元类** ，元类可以简称为类的类， **元类的主要目的是为了控制类的创建行为** 。

* `type` 是Python的一个内建元类，用来直接控制生成类，在Python中任何 `class` 定义的类其实都是 `type` 类实例化的结果。
* 只有继承了 `type` 类才能称之为一个元类，否则就是一个普通的自定义类，自定义元类可以控制类的产生过程，类的产生过程其实就是元类的调用过程。

#### 小结

元类的各种操作可以实现类的验证和注册逻辑，均可以在元类的 `__new__` 方法中实现，主要原因是当子类对象构建时，会先调用元类的 `__new__` 方法，产生一个空对象，然后再调用子类的 `__init__` 方法给对象属性进行赋值。

### 验证子类

> 元类是python比较高级的用法，简而言之，元类就是创建类的类。
>
> 而type就是一个元类，是用来创建类对象的类。
>
> 因此，要定义元类就要使其继承type类。
>
> 通常情况下，开发者在使用OOP的方式编程时，往往会用到\_\_init\_\_方法，即构造函数。
>
> 该方法会在类初始化时运行。但是我们可以将验证的时机提前，以至于提前到类创建之时，因此就会用到\_\_new\_\_方法。

```python
class Base(type):
    def __new__(cls, name, param, dicts):
        print(cls)
        print(name)
        print(param)
        print(dicts)
        return super().__new__(cls, name, param, dicts)
 
class Meta(metaclass=Base):
    name = 'yang'
 
    def person(self):
        pass
Meta()
 
<class '__main__.Base'>
Meta
()
{'__module__': '__main__', '__qualname__': 'Meta', 'name': 'yang', 'person': <function Meta.person at 0x10c6492f0>}
```

**元类中所编写的验证逻辑，针对的是该基类的子类，而不是基类本身**。

> `__new__()`方法接收到的参数依次是：
>
> 1. 当前准备创建的类的对象；
> 2. 类的名字；
> 3. 类继承的父类集合；
> 4. 类的方法集合。

> **案例1** ：编写一个多边形类，当边数小于 `3` 时，其类报错，实现其验证逻辑。

```python
class ValidatePolygon(type):
    ## __new__当中放入验证逻辑
    def __new__(meta,name,bases,class_dict):
        if bases!=(object,): ##针对子类而不对基类
            if class_dict['sides'] < 3:
                raise ValueError('Polygons need 3+ sides')
        return type.__new__(meta,name,bases,class_dict)
        
class Polygons(object,metaclass=ValidatePolygon):
    sides = None
    
    @classmethod
    def interior_angles(cls):
        return (cls.sides - 2) * 180
        
class Triangle(Polygons):
    sides = 3
    
### 类设计报错。
class Line(Polygons):
    sides = 1
```

### 注册子类

元类还有一个用途，就是在程序中 `自动注册类型` 。开发者每次从基类中继承子类时，基类的元类都可以自动运行注册代码。

> **案例2** ：实现对象的序列化与反序列化

```python
###建立类名与该类对象的映射关系，维护registry字典。
registry = {}
def register_class(target_class):
    registry[target_class.__name__] = target_class

def deserialize(data):
    params = json.loads(data)
    name = params['class']
    target_class = registry[name]
    return target_class

class Meta(type):
    def __new__(meta,name,bases,class_dict):
        cls = type.__new__(meta,name,bases,class_dict)
        register_class(cls) ##注册子类
        return cls

class BetterSerializable(object):
    def __init__(self,*args):
        self.args = args
    
    def serialize(self):
        return json.dumps({'class':self.__class__.__name__,
				            'args':self.args,})
    def __repr__(self):
        pass

class RegisterSerializabel(BetterSerializable,metaclass=Meta):
    pass
```

通过元类来实现类的注册，可以确保所有的子类都不会遗漏，从而避免后续的错误。

### 获取\_\_init\_\_的默认参数

> 获取\_\_init\_\_的默认参数，并在classmethod方法中为没有给定的属性赋默认值，提升代码的健壮性
>
> 元类定义：
>
> ```python
> #!/usr/bin/env Python
> # -- coding: utf-8 --
>
> """
> @version: v0.1
> @author: narutohyc
> @file: meta_interface.py
> @Description: 
> @time: 2020/6/15 20:29
> """
> import collections
> from abc import (ABC,
>                  abstractmethod,
>                  ABCMeta)
> import inspect
>
> class DicMetaClass(ABCMeta):
>     def __new__(cls, name, bases, attrs, **kwargs):
>         if name == 'DicMeta':
>             return super().__new__(cls, name, bases, attrs, **kwargs)
>         # 获取__init__函数的 默认值
>         argspec = inspect.getfullargspec(attrs["__init__"])
>         init_defaults = dict(zip(argspec.args[-len(argspec.defaults):], argspec.defaults))
>         cls.__init_defaults = init_defaults
>         attrs['__init_defaults__'] = init_defaults
>         return super().__new__(cls, name, bases, attrs, **kwargs)
> ```
>
> 抽象父类：
>
> ```python
> #!/usr/bin/env Python
> # -- coding: utf-8 --
>
> """
> @version: v0.1
> @author: narutohyc
> @file: meta_interface.py
> @Description: 
> @time: 2020/6/15 20:29
> """
>
> from abc import (ABC,
>                  abstractmethod,
>                  ABCMeta)
>
> class DicMeta(ABC, metaclass=DicMetaClass):
>     def __init__(self):
>         pass
>
>     @abstractmethod
>     def to_dict(self):
>         '''
>         返回字典
>         '''
>         pass
>
>     @classmethod
>     def load_from_mapping(cls, mapping_datas):
>         '''
>         用字典来构建实例对象
>         '''
>         assert isinstance(mapping_datas, collections.abc.Mapping)
>         obj = cls.__new__(cls)
>         [setattr(obj, k, v) for k, v in mapping_datas.items()]
>         return obj
> ```
>
> 子类实现：
>
> ```python
> #!/usr/bin/env Python
> # -- coding: utf-8 --
>
> """
> @version: v0.1
> @author: narutohyc
> @file: text_meta.py
> @Description: 
> @time: 2020/5/22 14:55
> """
> from augmentation.meta_class.meta_interface import DicMeta
> from utils.utils_func import gen_md5, str2bool
> import re
>
> class TaskMeta(DicMeta):
>     '''
>     数据包装类的bean结构
>     '''
>     def __init__(self, text, doc_id, sentence_id, reg_lst,
>                  has_reg=True,
>                  flag=None,
>                  dataset='train',
>                  text_source="primitive"):
>         super(TaskMeta, self).__init__()
>         self.text = text
>         self.doc_id = doc_id
>         self.sentence_id = sentence_id
>         if reg_lst and isinstance(reg_lst[0], list):
>             reg_lst = ['%s %s %s' % (tag, start_idx, value) for tag, start_idx, value in reg_lst]
>         self.reg_lst = sorted(reg_lst, key=lambda reg: int(re.sub(' +', ' ', reg).split(" ", 2)[1])) if reg_lst else []
>         self.flag = list(set(i.split(' ', 2)[0] for i in self.reg_lst)) if flag is None else flag
>         self.has_reg = str2bool(has_reg)
>         self.dataset = dataset
>         self.text_source = text_source
>         self._id = gen_md5(self.text)
>
>     @classmethod
>     def load_from_mapping(cls, mapping_datas):
>         '''
>         用字典来构建 TaskMeta实例
>         '''
>         obj = super(TaskMeta, cls).load_from_mapping(mapping_datas)
>         obj._id = gen_md5(obj.text)
>         [setattr(obj, k, v) for k, v in obj.__init_defaults__.items() if not hasattr(obj, k)]
>         if obj.flag is None:
>             obj.flag = list(set(i.split(' ', 2)[0] for i in obj.reg_lst))
>         obj.has_reg = str2bool(obj.has_reg)
>         return obj
>     
>     @property
>     def to_dict(self):
>         '''
>         当该类没有其他多余属性时  可以直接返回self.__dict__的副本
>         '''
>         return {"text": self.text,
>                 "doc_id": self.doc_id,
>                 "sentence_id": self.sentence_id,
>                 "reg_lst": self.reg_lst,
>                 "flag": list(self.flag),
>                 "has_reg": self.has_reg,
>                 "dataset": self.dataset,
>                 "text_source": self.text_source,
>                 "_id": self._id}    
> ```
>
> 测试类：
>
> ```python
> task_meta_0 = TaskMeta.load_from_mapping({'text': '斯坦福大学开发的基于条件随机场的命名实体识别系统，该系统参数是基于CoNLL、MUC-6、MUC-7和ACE命名实体语料训练出来的。', 
>                                           'doc_id': 'id1', 'sentence_id': 'id1', 
>                                           'reg_lst': ['学校 0 斯坦福大学', '标注 33 CoNLL', '标注 39 MUC-6', '标注 45 MUC-7', '标注 51 ACE']})
> task_meta_1 = TaskMeta.load_from_mapping({'text': '斯坦福大学开发的基于条件随机场的命名实体识别系统，该系统参数是基于CoNLL、MUC-6、MUC-7和ACE命名实体语料训练出来的。', 
>                                           'doc_id': 'id1', 'sentence_id': 'id1', 
>                                           'reg_lst': ['学校 0 斯坦福大学', '标注 33 CoNLL', '标注 39 MUC-6', '标注 45 MUC-7', '标注 51 ACE'], 
>                                           'flag': ['学校', '标注'], 'has_reg': True, 
>                                           'dataset': 'train', 'text_source': 'primitive', '_id': '3b895befc659345be8686bd7de4d7693'})
> task_meta_0.to_dict == task_meta_1.to_dict
> Out[33]: True
> ```
>
> 可以看出，task\_meta\_0和task\_meta\_1两者的 值是完全相同的，这里就可以做到共享\_\_init\_\_默认参数的效果

### 注解类的属性

> 元类还有一个更有用处的功能，那就是可以在某个类刚定义好但是尚未使用的时候，提前修改或注解该类的属性。这种写法通常会与描述符(descriptor) 搭配起来(参见本书第31条)，令这些属性可以更加详细地了解自己在外围类中的使用方式。 例如，要定义新的类，用来表示客户数据库里的某- -行。同时，我们还希望在该类的相关属性与数据库表的每一列之间， 建立对应关系。于是，用下面这个描述符类，把属性与列名联系起来。
>
> ```python
> class Field:
>        def __init__(self, name):
>            self.name = name
>            self.internal_name = '_' + self.name
>
>        def __get__(self, instance, owner):
>            if instance is None:
>                return self
>            return getattr(instance, self.internal_name, '')
>
>        def __set__(self, instance, value):
>            setattr(instance, self.internal_name, value)
> ```

> 由于列的名称已经保存到了Field描述符中，所以我们可以通过内置的setattr和getattr函数，把每个实例的所有状态都作为protected字段，存放在该实例的字典里面。 在本书前面的例子中，为了避免内存泄漏，我们曾经用weakref字典来构建描述符，而刚才的那段代码，目前看来，似乎要比weakref方案便捷得多。 接下来定义表示数据行的Customer类，定义该类的时候，我们要为每个类属性指定对应的列名。
>
> ```python
> class Customer:
>        first_name = Field('first_name')
>        last_name = Field('last_name')
>        prefix = Field('prefix')
>        suffix = Field('suffix')
> ```
>
> 问题在于，上面这种写法显得有些重复。在Customer类的class语句体中，我们既然要将构建好的Field对象赋给Customer.first\_ name, 那为什么还要把这个字段名(本例中是'first\_ name') 再传给Field的构造器呢? 之所以还要把字段名传给Field构造器，是因为定义Customer类的时候，Python 会以从右向左的顺序解读赋值语句，这与从左至右的阅读顺序恰好相反。首先，Python 会以Field(first\_ name') 的形式来调用Field 构造器，然后，它把调用构造器所得的返回值，赋给Customer.field\_ name。 从这个顺序来看，Field 对象没有办法提前知道自己会赋给Customer类里的哪一个属性。 为了消除这种重复代码，我们现在用元类来改写它。使用元类，就相当于直接在class语句上面放置挂钩，只要class语句体处理完毕，这个挂钩就会立刻触发。于是，我们可以借助元类，为Field描述符自动设置其Field.name和Field.internal\_ name, 而不用再像刚才那样，把列的名称手工传给Field 构造器。
>
> ```python
> class Meta(type):
>        def __new__(meta, name, bases, class_dict):
>            for key, value in class_dict.items():
>                if isinstance(value, Field):
>                    value.name = key
>                    value.internal_name = '_' + key
>            cls = type.__new__(meta, name, bases, class_dict)
>            return cls
> ```

> 下面定义一一个基类，该基类使用刚才定义好的Meta作为其元类。凡是代表数据库里面某一行的类，都应该从这个基类中继承，以确保它们能够利用元类所提供的功能:
>
> ```python
> class DatabaseRow(object, metaclass=Meta):
> 	pass
> ```
>
> 采用元类来实现这套方案时，Field 描述符类基本上是无需修改的。唯一 要调整的地方就在于:现在不需要再给构造器传人参数了，因为刚才编写的Meta.\_\_new\_\_ 方法会自动把相关的属性设置好。
>
> ```python
> class Field:
>        def __init__(self):
>            self.name = None
>            self.internal_name = None
> ```
>
> 有了元类、新的DatabaseRow基类以及新的Field描述符之后，我们在为数据行定义DatabaseRow子类时，就不用再像原来那样，编写重复的代码了。
>
> ```python
> class BetterCustomer(DatabaseRow):
>        first_name = Field()
>        last_name = Field()
>        prefix = Field()
>        suffix = Field()
> ```

#### ORM例子

> ORM 是 python编程语言后端web框架 Django的核心思想，“Object Relational Mapping”，即对象-关系映射，简称ORM。 一个句话理解就是：创建一个实例对象，用创建它的类名当做数据表名，用创建它的类属性对应数据表的字段，当对这个实例对象操作时，能够对应MySQL语句

代码

```python
class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, tuple):
                print('Found mapping :%s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings
        attrs['__table__'] = name
        return type.__new__(cls, name, bases, attrs)


class Model(object, metaclass=ModelMetaclass):
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields, args = [], []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, None))
        args_temp = list()
        for temp in args:
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)

        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print(sql)

class User(Model):
    uid = ('uid', 'int unsigned')
    name = ('username', 'varchar(30)')
    email = ('email', 'varchar(30)')
    password = ('password', 'varchar(30)')

user = User(uid=1234, name='naruto', email='1832044042@qq.mail', password='hycpass')
user.save()
```

输出

```python
Found mapping :uid ==> ('uid', 'int unsigned')
Found mapping :name ==> ('username', 'varchar(30)')
Found mapping :email ==> ('email', 'varchar(30)')
Found mapping :password ==> ('password', 'varchar(30)')
insert into User (uid,username,email,password) values (1234,'naruto','1832044042@qq.mail','hycpass')
```

#### 要点

> 借助元类，我们可以在某个类完全定义好之前，率先修改该类的属性。
>
> 描述符与元类能够有效地组合起来，以便对某种行为做出修饰，或在程序运行时探查相关信息。
>
> 如果把元类与描述符相结合，那就可以在不使用weakref模块的前提下避免内存泄漏。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://narutohyc.gitbook.io/python/7.python-yuan-bian-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
