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()




完結

下一篇 迭代器和生成器

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