Python中的元类编程

2018 年 10 月 12 日 • 阅读数: 108

Python中的元类编程

属性方法(动态属性)

  • 通过@property装饰器可以将一个方法变成一个属性
  • 可以通过对象名.方法名的方式获取到返回值
  • 传参的话需要通过setter方法,在外部通过赋值语句传递参数
from datetime import date,datetime

class User(object):
    def __init__(self,name,birthday):
        self.name = name
        self.birthday = birthday
        self._age = 0
        
    @property
    def age(self):
        return datetime.now().year - self.birthday.year
    
    @age.setter
    def age(self,value):
        self._age = value
user = User('amor',date(1996,8,23))
print(user.age)
user.age = 30
print(user._age)
22
30

getattr和getattribute

  • getattr 在使用对象调用属性时,没有找到对应属性时调用
  • getattribute 在使用对象调用属性时,无条件调用该方法
class People(object):
    age = 20
    def __init__(self,**kwargs):
        self.info = kwargs
    
    def __getattr__(self,item):
        return self.info[item]
    
    def __getattribute__(self,item):
        return 'amor'
    
    def __repr__(self):
        return str(self.info)
people = People(name='amor',age=21,height=175)
people.age 
'amor'

属性描述符

  • 描述符:是实现了特定协议的类
  • 描述符功能:是对多个属性运用相同存取逻辑的一种方式
  • 描述符协议:包括__get__、set、和__delete__方法,通常只实现部分协议
  • 大多数描述符只实现了__get__和__set__方法,但是property类实现了完整的描述符协议
  • 只实现了__get__方法的,叫非数据描述符,其他的也称数据描述符
  • 查找优先级:数据描述符 > 对象属性 > 非数据描述符 > 类属性 > getattr
import numbers
class IntField(object):
    def __get__(self,instance,owner):
        return self.value
    def __set__(self,instance,value):
        if not isinstance(value,numbers.Integral):
            raise ValueError("int value need")
        self.value = value
    def __delete__(self,instance):
        pass
class User(object):
    age = IntField()
"""
如果user是某个类的实例,那么user.age(以及等价的getattr(user,'age'))
首先调用__getattribute__,如果定义了__getattr__方法
那么在__getattribute__抛出 AttributeError 的时候就会调用__getattr__,
而对于描述符(__get__)的调用,则是发生在__getattribute__内部的
user = User(),那么 user.age 的调用顺序如下:
(1)如果"age"是出现在User类或其基类的__dict__中,且age是数据描述符,那么调用其__get__方法
(2)如果"age"出现在user对象的__dict__中,那么直接返回user.__dict__['age']
(3)如果"age"出现在User或其基类的__dict__中
(3.1)如果"age"是非数据描述符,那么调用其__get__方法
(3.2)否则返回__dict__['age']
(4)如果User有__getattr__方法,调用__getattr__方法
(5)否则抛出AttributeError
"""
user = User()
user.age = 22
user.age
22

new和init

  • new是用来控制对象的生成过程,在对象生成之前
  • init是用来完善对象的
  • 如果new方法不返回对象,则不会调用init方法
class User(object):
    def __new__(cls,*args,**kwargs):
        print('new')
        return super().__new__(cls)
    def __init__(self,name):
        self.name = name
        print('init')
user = User('amor')
new
init

元类编程

  • 元类就是创建类的类
  • 在创建类的过程中,如果不指定元类,就会默认调用type来创建一个类
  • 使用 metaclass 可以指定元类
  • 自定义一个元类需要继承type
  • 自定义一个元类,最重要的就是重写new方法,可以自定义类的创建过程
  • 在类中添加一些自定义的属性,也可以做类型检测
'''通过函数来动态创建类'''
def create_class(name):
    if name == 'user':
        class User:
            def __str__(self):
                return 'user'
        return User
    elif name == 'company':
        class Company:
            def __str__(self):
                return 'company'
        return Company
MyClass = create_class('user')
obj = MyClass()
print(obj)
user
'''通过type来动态创建类,第一个参数类名,第二个参数一个基类的tuple,第三个参数一个属性的dict'''
def __str__(self):
    return 'i am user'
User = type("User",(object,),{"name":"amor",'__str__':__str__})
my_obj = User()
print(my_obj,my_obj.name)
i am user amor
'''自定义元类'''
class MetaClass(type):
    def __new__(cls,*args,**kwargs):
        print('MetaClass new fun')
        return super().__new__(cls,*args,**kwargs)
class User(metaclass=MetaClass):
    def __new__(cls,*args,**kwargs):
        print('User new fun')
        return super().__new__(cls)
    def __init__(self,name):
        print(name)
        
user = User('amor')
MetaClass new fun
User new fun
amor

实现简单ORM

class Field(object):
    pass
import numbers

class IntField(Field):
    
    def __init__(self,db_column,min_value=None,max_value=None):
        self._value = None
        self.db_column = db_column
        self.min_value = min_value
        self.max_value = max_value
        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')
        
    def __get__(self,instance,owner):
        return self._value
    
    def __set__(self,instance,value):
        if not isinstance(value,numbers.Integral):
            raise ValueError('int value need')
        elif value < self.min_value or value > self.max_value:
            raise ValueError('value must between min_value and max_value')
        self._value = 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_length for charfiled')
        self.max_length = max_length
            
    def __get__(self,instance,owner):
        return self._value
    
    def __set__(self,instance,value):
        if not isinstance(value,str):
            raise ValueError('string value need')
        elif len(value) > self.max_length:
            raise ValueError('value len excess len of max_length')
        self._value = value
class ModelMetaClass(type):
    def __new__(cls,name,base,attrs,**kwargs):
        if name == "BaseModel":
            return super().__new__(cls,name,base,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
        return super().__new__(cls,name,base,attrs,**kwargs)
class BaseModel(metaclass=ModelMetaClass):
    def __init__(self,*args,**kwargs):
        for key,value in kwargs.items():
            setattr(self,key,value)
        return super().__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'
user = User(name='amor',age=22)
user.save()
insert user(name,age) value(amor,22)
标签: Python元类编程

召唤伊斯特瓦尔