python元类编程

property动态属性

动态属性的设置
和动态属性的获取

from datetime import date,datetime
class User:
    def __init__(self,name,birthday):
        self.name=name
        self.birthday=birthday
        self._age=0
    #user= User("xiaopang", date(year=1996, month=3, day=8))
    # print("in{}file".format(__file__))
    #     def get_age(self):
    #         return datetime.now().year-self.birthday.year
    @property   #@property获取age属性
    def age(self):
        return datetime.now().year-self.birthday.year
    # @age.getter
    # def age(self):
    #     return self._age
    @age.setter
    def age(self,value):
        self._age=value

    # @age.setter
    # def age_set(self):
    #     return 23


if __name__=="__main__":
    user=User("xiaopang",date(year=1996,month=3,day=8))
    # inI:/ainlp/pythonHight/chapter08/property_test.pyfile
    print("in{}file".format(__file__))
    # print(user.get_age())  ##23
    print(user.age)  #23
    user.age=23
    print(user.age)

property源码:


    def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
        """
        property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
        
        fget is a function to be used for getting an attribute value, and likewise
        fset is a function for setting, and fdel a function for del'ing, an
        attribute.  Typical use is to define a managed attribute x:
        
        class C(object):
            def getx(self): return self._x
            def setx(self, value): self._x = value
            def delx(self): del self._x
            x = property(getx, setx, delx, "I'm the 'x' property.")
        
        Decorators make defining new properties or modifying existing ones easy:
        
        class C(object):
            @property
            def x(self):
                "I am the 'x' property."
                return self._x
            @x.setter
            def x(self, value):
                self._x = value
            @x.deleter
            def x(self):
                del self._x
        
        # (copied from class doc)
        """
        pass

__getattr__和__getattribute__魔法函数

  1. __ getattr __ ():


#__getattr__,getattribute__
#__getattr__就是在查找不到属性的时候调用
from datetime import date
class User:
    def __init__(self,name,birthday,info={}):
        self.Name=name
        self.birthday=birthday
        self.info=info
    #如果可以查找到某个属性,则不会调用这个函数,
    #否则就会执行这个函数
    # def  __getattr__(self, item):
    #     # print("not find attr")
    #     # return self.Name
    #     return self.info[item]
if __name__=="__main__":
    user=User("xiaopang",date(year=1996,month=3,day=8),info={"company_name":"yuzhou"})
    #如果不存在属性  AttributeError: 'User' object has no attribute '_age'
    # 但是如果在类里面加入__getattr__这个魔法函数不会报错,会执行
    #这个函数里面的逻辑
    # print(user._age)# not find attr   None
    # print(user.name)
    print(user.company_name) #有__getattr__ 输出结果为    yuzhou
    # 如果里面没有__getattr__,则会报错
    #AttributeError: 'User' object has no attribute 'company_name'




  1. __ getattribute__()里面实现了__getattr__()
    注意: __ getattribute__()这个函数如果要重写
    会修改类里面的执行顺序,容易造成混乱,需要自己搞清楚里面的
    内在逻辑
class User:
    def __init__(self,info={}):
        self.info=info
    def __getattribute__(self, item):
        return " i miss you"
if __name__=="__main__":
    user=User(info={"company_name":"yuzhou","name":"xiaopang","age":23})
    print(user.company_name)#i miss you
    print(user.name)#i miss you
    print(user.age)#i miss you



#说明了__ getattribute __ ()实在 __ init __ ()初始化之后
不管有查找到属性都会进入的魔法函数:\

属性描述符和属性查找过程

属性描述符:

只要实现__get__、set、__delete__方法中的一个就可以认为是描述符;
只实现__get__方法的对象是非数据描述符,在初始化之后它们只能被读取;
同时实现__get__和__set__的对象是数据描述符,这种属性是可读写的。



from datetime import date,datetime
import numbers
'''
只要实现__get__、set、__delete__方法中的一个就可以认为是描述符;
只实现__get__方法的对象是非数据描述符,在初始化之后它们只能被读取;
同时实现__get__和__set__的对象是数据描述符,这种属性是可读写的。
'''


#一个类里如果包含下面__get__、__set_、___delete__
#这3个魔法函数当中的任意一个函数,那么这个类就
# 是属性描述符

class DataIntField: #数据描述符
    def __get__(self,instance,owner):
        return self.value

    def __set__(self, instance, value):
        #判断age是否是数字类型的
        if not isinstance(value,numbers.Integral):
            raise ValueError("int value need")
        if value<0 :
            raise ValueError("age must be More than 0")
        else:
            self.value=value

    def __delete__(self, instance):
        pass
class NonDataIntField:  #非数据属性描述符

    def __get__(self,instance,owner):
        return self.value

class User:
    #age是一个属性描述符的对象
    # age=DataIntField()
    age=NonDataIntField()

'''
属性的查找顺序:

如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
首先调用__getattribute__。如果类定义了__getattr__方法,
那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,
而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
user = User(), 那么user.age 顺序如下:

(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor,
那么调用其__get__方法, 否则

(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则

(3)如果“age”出现在User或其基类的__dict__中

(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则

(3.2)返回 __dict__[‘age’]

(4)如果User有__getattr__方法,调用__getattr__方法,否则

(5)抛出AttributeError

'''
# class User:
#     def __init__(self,name,email,birthday):
#         self.name=name
#         self.email=email
#         self.birthday=birthday
#         self._age=0
#     #user= User("xiaopang", date(year=1996, month=3, day=8))
#     # print("in{}file".format(__file__))
#     #     def get_age(self):
#     #         return datetime.now().year-self.birthday.year
#     @property   #@property获取age属性
#     def age(self):
#         return datetime.now().year-self.birthday.year
#     @age.getter
#     def age(self):
#         return self._age
#     @age.setter
#     def age(self,value):
#         #检查是否是字符串类型
#         self._age=value
#
#
#     # @age.setter
#     # def age_set(self):
#     #     return 23


if __name__=="__main__":
    # user=User("xiaopang",date(year=1996,month=3,day=8))
    # # inI:/ainlp/pythonHight/chapter08/property_test.pyfile
    # print("in{}file".format(__file__))
    # # print(user.get_age())  ##23
    # print(user.age)  #23
    # user.age=23
    # print(user.age)
    user=User()
    # user.age=30
    # user.age=-1
    # print(user.age)
    # print(getattr(user,"age")) #30   等价于user.age
    # print(user.__dict__)
    # print(user.age)
    print("..............................")
    user.__dict__["age"]="xiaopang"
    print(user.__dict__)
    print(user.age)
    pass
'''

'''




user. __ dict __ 这个属性是属于实例的 这样获取属性的顺序逻辑和通过使用
user.age这种逻辑是不一样的。
user. __ dict__直获取那个__dict__属性值,而不会走user.age查找顺序的流程。
user.age这种逻辑是不一样的,这里会直接寻找 __ getattribue __ 里面的
逻辑,
1.首先会寻找类或基类里面的数据属性属性描述符,如果没有
2.找到则实例的属性当中寻找,如果么没有
有则会寻找__getattr__
3.类或基类当中的属性__dict__当中
         3.1非数据描述符,那么__get__
         3.2返回 __ dict __ [‘age’]
如果都没有找到
4.如果User有__getattr__方法,调用__getattr__方法,否则
抛出异常抛出AttributeError

‘’’

属性的查找顺序:

如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
首先调用__getattribute__。如果类定义了__getattr__方法,
那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,
而对于描述符(get)的调用,则是发生在__getattribute__内部的。
user = User(), 那么user.age 顺序如下:

(1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor,
那么调用其__get__方法, 否则

(2)如果“age”出现在user的__dict__中, 那么直接返回 obj.dict[‘age’], 否则

(3)如果“age”出现在User或其基类的__dict__中

(3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则

(3.2)返回 dict[‘age’]

(4)如果User有__getattr__方法,调用__getattr__方法,否则

(5)抛出AttributeError

‘’’

__ new __ 和 __ init __

class User:
    #python 2.2之后
    #new是用来控制对象的生成过程,在对象生成前
    #init是用来完善对象的

    #如果new方法不返回对象则不调用init函数
    def __new__(cls,*args,**kwargs):
        print("in new")
        return super().__new__(cls)

    def __init__(self,name):
        self.name=name
if __name__ =="__main__":
    user =User(name="xiaopang")


自定义元类

元类的概念:元类就是创建类的类。

对象 < – class(对象) < – type

首先考虑如何动态的创建类:

在这里插入代码片
#类也是对象,type是创建类的类

#动态的创建类,如何来做?
def create_class(name):
    if name=="user":
        class User:
            # def __str__(self):
            #     return "user"
            def createName(self):
                return "xiaopang"
        return User
    elif name=="companpy":
        class Company:
            def __str__(self):
                return "companpy"
        return Company
if __name__=="__main__":
    MyClass=create_class("user")
    myjob=MyClass()
    print(myjob.createName())

上面代码的写法还是需要我们去写class语句,稍微麻烦些,如何
让它更简洁呢?涉及到type了 另外在python当中 type是可以用来
创建类的
user=type(“类名”,(继承的基类,…),{“包含的属性”:“属性值”,…})


#如何定义使用type创建的类中的方法
def say(self):
    return " xiaopang is an eater"
#type使用type动态创建类
# User=type("User",(),{})
if __name__=="__main__":

    User = type("User", (), {"name":"xiaopang","say":say})
    myjob=User()
    print(User)#<class '__main__.User'>
    print(myjob)#<__main__.User object at 0x0000000002D90128>
    print(myjob.name)#xiaopang
    #调用它里面的方法
    print(myjob.say()) # xiaopang is an eater

#如何定义使用type创建的类中的方法
def say(self):
    return " xiaopang is an eater"
#让动态创建的类继承Baseclass这个类   
class Baseclass(object):
    def answer(self):
        return " i like xiaopang"

#type使用type动态创建类
# User=type("User",(),{})
if __name__=="__main__":
    # MyClass=create_class("user")
    # myjob=MyClass()
    # print(myjob.createName())
    User = type("User", (Baseclass,), {"name":"xiaopang","say":say})
    myjob=User()
    print(User)#<class '__main__.User'>
    print(myjob)#<__main__.User object at 0x0000000002D90128>
    print(myjob.name)#xiaopang
    #调用它里面的方法
    print(myjob.say()) # xiaopang is an eater
    print(myjob.answer()) #i like xiaopang
    

在python3当中可以这样使用:

class MetaClass(type):
          pass
class User(metaclass=MetaClass):
          pass
MataClass创建的目的就是为了控制User这个类实例化的过程,为什么
它能够控制类的实例化过程呢?
python中类的实例化过程:
在默认情况下type去创建类对象,如果这个类中使用了元类,在类的实例化的过程中那么会首先寻找元类,通过元类(metaclass)去创建User类 。


class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls,*args, **kwargs)
        pass
class User(metaclass=MetaClass):
    #MataClass创建的目的就是为了控制User这个类实例化的过程
    def __init__(self,name):
        self.name=name
    def __str__(self):
        return "user"



if __name__=="__main__":
    user=User(name="xiaopang")
    print(user)#<__main__.User object at 0x0000000002D76780>
    print(user.name) #xiaopang

    pass

查看源码:

from collections.abc import *
from _collections_abc import all

Iterable
class Iterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__")
        return NotImplemented

ABCMeta这个类的源码:

class ABCMeta(type):

    """Metaclass for defining Abstract Base Classes (ABCs).

    Use this metaclass to create an ABC.  An ABC can be subclassed
    directly, and then acts as a mix-in class.  You can also register
    unrelated concrete classes (even built-in classes) and unrelated
    ABCs as 'virtual subclasses' -- these and their descendants will
    be considered subclasses of the registering ABC by the built-in
    issubclass() function, but the registering ABC won't show up in
    their MRO (Method Resolution Order) nor will method
    implementations defined by the registering ABC be callable (not
    even via super()).

    """

    # A global counter that is incremented each time a class is
    # registered as a virtual subclass of anything.  It forces the
    # negative cache to be cleared before its next use.
    # Note: this counter is private. Use `abc.get_cache_token()` for
    #       external code.
    _abc_invalidation_counter = 0

    def __new__(mcls, name, bases, namespace, **kwargs):
        cls = super().__new__(mcls, name, bases, namespace, **kwargs)
        # Compute set of abstract method names
        abstracts = {name
                     for name, value in namespace.items()
                     if getattr(value, "__isabstractmethod__", False)}
        for base in bases:
            for name in getattr(base, "__abstractmethods__", set()):
                value = getattr(cls, name, None)
                if getattr(value, "__isabstractmethod__", False):
                    abstracts.add(name)
        cls.__abstractmethods__ = frozenset(abstracts)
        # Set up inheritance registry
        cls._abc_registry = WeakSet()
        cls._abc_cache = WeakSet()
        cls._abc_negative_cache = WeakSet()
        cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        return cls

    def register(cls, subclass):
        """Register a virtual subclass of an ABC.

        Returns the subclass, to allow usage as a class decorator.
        """
        if not isinstance(subclass, type):
            raise TypeError("Can only register classes")
        if issubclass(subclass, cls):
            return subclass  # Already a subclass
        # Subtle: test for cycles *after* testing for "already a subclass";
        # this means we allow X.register(X) and interpret it as a no-op.
        if issubclass(cls, subclass):
            # This would create a cycle, which is bad for the algorithm below
            raise RuntimeError("Refusing to create an inheritance cycle")
        cls._abc_registry.add(subclass)
        ABCMeta._abc_invalidation_counter += 1  # Invalidate negative cache
        return subclass

    def _dump_registry(cls, file=None):
        """Debug helper to print the ABC registry."""
        print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
        print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
        for name in sorted(cls.__dict__.keys()):
            if name.startswith("_abc_"):
                value = getattr(cls, name)
                print("%s: %r" % (name, value), file=file)

    def __instancecheck__(cls, instance):
        """Override for isinstance(instance, cls)."""
        # Inline the cache checking
        subclass = instance.__class__
        if subclass in cls._abc_cache:
            return True
        subtype = type(instance)
        if subtype is subclass:
            if (cls._abc_negative_cache_version ==
                ABCMeta._abc_invalidation_counter and
                subclass in cls._abc_negative_cache):
                return False
            # Fall back to the subclass check.
            return cls.__subclasscheck__(subclass)
        return any(cls.__subclasscheck__(c) for c in {subclass, subtype})

    def __subclasscheck__(cls, subclass):
        """Override for issubclass(subclass, cls)."""
        # Check cache
        if subclass in cls._abc_cache:
            return True
        # Check negative cache; may have to invalidate
        if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
            # Invalidate the negative cache
            cls._abc_negative_cache = WeakSet()
            cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
        elif subclass in cls._abc_negative_cache:
            return False
        # Check the subclass hook
        ok = cls.__subclasshook__(subclass)
        if ok is not NotImplemented:
            assert isinstance(ok, bool)
            if ok:
                cls._abc_cache.add(subclass)
            else:
                cls._abc_negative_cache.add(subclass)
            return ok
        # Check if it's a direct subclass
        if cls in getattr(subclass, '__mro__', ()):
            cls._abc_cache.add(subclass)
            return True
        # Check if it's a subclass of a registered class (recursive)
        for rcls in cls._abc_registry:
            if issubclass(subclass, rcls):
                cls._abc_cache.add(subclass)
                return True
        # Check if it's a subclass of a subclass (recursive)
        for scls in cls.__subclasses__():
            if issubclass(subclass, scls):
                cls._abc_cache.add(subclass)
                return True
        # No dice; update negative cache
        cls._abc_negative_cache.add(subclass)
        return False

在使用抽象基类的时候,其实上面抽象基类Iterable就是用了元类ABCMeta,ABCmeta中 其实是重写了__new__方法,在我们继承Iterable这个抽象基类有没有实现或者重写抽象基类中的所有抽象方法(abstractmethod ),可以在元类里面进行检查,如果不通过则会抛出未实现抽象基类当中的所有abstractmethod方法。因为继承抽象基类的类在实例化的过程中必须实现重写所有abstractmethod方法,如果不实现就应该抛出异常。元类可以控制类的实例化过程就是这样子的。


介绍一下
getattr()

	def getattr(object, name, default=None): # known special case of getattr
	    """
	    getattr(object, name[, default]) -> value
		    
	    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
	    When a default argument is given, it is returned when the attribute doesn't
	    exist; without it, an exception is raised in that case.
    """

元类简单实现orm

# 需求
import  numbers

class Field:
    pass


#为了判断字段的类型,需要使用属性描述符
class DataIntField(Field):
    def __init__(self,db_column,min_value=None,max_value=None):
        self._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 postive int")
        if max_value is not None:
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif min_value < 0:
                raise ValueError("max_value must be postive int")
        if min_value is not None and max_value is not None:
            if  min_value>max_value:
                raise ValueError("min_value musy 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")
        if value<self.min_value or value >self.max_value:
            raise ValueError("value must be between min_value and max_value")

        self._value=value

class DataCharField(Field):
    def __init__(self,db_column,max_length=None):
        self._value=None
        self.db_column=db_column
        self.max_length=max_length
        if max_length is None:
            raise ValueError("you must spcify max_length for charfield")

    def __get__(self, instance, owner):
        return self._value
    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("str value need")
        if len(value)  > self.max_length:
            raise ValueError("value len must be shorter than max_length")
        self._value = value

# 元类
class ModelMetaClass(type):
    #                name类名   bases 继承的类   attr 类的属性
    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,"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):
    #这个父类不知道用户传递什么参数 *args  **kwargs

    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 user(name,age) value('xiaopang',23) "  应该是动态的
        sql="insert {db_table}({fields}) value({values})"\
            .format(db_table=self._meta['db_table'],fields=","\
            .join(fields),values=",".join(values))

        #这里没写具体插入数据库当中操作
        pass
class User(BaseModel):
    # def __init__(self):
    #     pass
    name=DataCharField(db_column="name",max_length=10)
    age=DataIntField(db_column="age",min_value=0,max_value=100)

    class Meta:
        db_table="user"

if __name__=="__main__":
    user=User(name="xiaopang",age=23)
    user.name="xiaopang"
    user.age=age=23
    user.save()




完结

下一篇 迭代器和生成器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章