經過類模擬一個classmthod

首先搞明白clssmethod原理,直接修改類的dictpython

框架以下:ruby

class Class_Method:
    def __init__(self,fn):
        self.fn = fn
    def __get__(self, instance, owner):
        print(self,instance,owner)
        return self.fn
class A:
    @Class_Method
    def bar(cls):
        print(cls.__name__)
f = A.bar
print(f)
f()


發現報錯:框架

Traceback (most recent call last):
<__main__.Class_Method object at 0x0000000000DE8390> None <class '__main__.A'>
  File "E:/python_project/learing/20171106/class_test.py", line 32, in <module>
<function A.bar at 0x00000000010F0488>
    f()
TypeError: bar() missing 1 required positional argument: 'cls'

get能夠獲取到,return的是bar函數
ide

考慮A在哪裏能夠獲取到,回到get方法中,獲取owner屬性,直接return owner函數

也就是說直接返回A測試

def __get__(self, instance, owner):
    print(self,instance,owner)
    return self.fn(owner)

測試:
ui

f()spa

Traceback (most recent call last):
  File "E:/python_project/learing/20171106/class_test.py", line 33, in <module>
    f()
TypeError: 'NoneType' object is not callable

是一個函數調用,可是沒有return任何東西
get

1.首先return self.fn(owner)  直接指向A類,A類能夠獲取it

2.可是函數返回值是None,由於是def bar中沒有return其餘,默認是None

解決:

固定返回參數

partial

return self.fn 和 class也就是A  返回爲一個新的函數

class Class_Method:
    def __init__(self,fn):
        print('init,self.fn : ', fn)
        self.fn = fn
    def __get__(self, instance,cls):
        print(self,instance,cls)
        # return self.fn(owner)
        return partial(self.fn,cls)
class A:
    @Class_Method
    def bar(cls):
        print('bar:',cls.__name__)
f = A.bar
print(f)
f()

返回以下:

init,self.fn :  <function A.bar at 0x00000000010C1488>
<__main__.Class_Method object at 0x00000000008592E8> None <class '__main__.A'>
functools.partial(<function A.bar at 0x00000000010C1488>, <class '__main__.A'>)
bar: A

由於返回的必須是函數,因此最好使用partial 返回一個新函數

再進行調用,發現模擬使用方法是差很少的,可是模仿終歸模仿

對實例的數據進行校驗

涉及:inspect 參數檢查

使用描述器進行參數檢查

當實例化的時候必須傳遞參數進去,必須實例化

class Typed:
    def __init__(self):
        pass
    def __get__(self, instance, owner):
        print('get :',self, instance, owner )
    def __set__(self, instance, value):
        print('set : ',self, instance, value)
class Person:
    name = Typed()
    age = Typed()
    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age

調用返回以下:

set :  <__main__.Typed object at 0x0000000000DD8470> <__main__.Person object at 0x0000000000DB6470> tom
set :  <__main__.Typed object at 0x0000000000DB6400> <__main__.Person object at 0x0000000000DB6470> 1

返回了兩個實例的value

判斷類型:

首先須要聲明並傳遞一個類型,

class Typed:
    def __init__(self,type):
        self.type = type
    def __get__(self, instance, owner):
        print('get :',self, instance, owner )
    def __set__(self, instance, value):
        print('set : ',self, instance, value)
class Person:
    name = Typed(str)
    age = Typed(int)
    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age
p = Person('tom',1)

獲取了value,接下來本身的類型也明確了,首先傳遞進來是被set攔截,那麼須要在set中作判斷

由於設置值的時候才觸發set

改進:

經過inspect 進行參數檢查

inspect.signature(Person)  檢查參數

print(inspect.signature(Person))
(name:str, age:int)

返回的是初始化方法,參數註解

經過parameters 返回的是一個

print(inspect.signature(Person).parameters)
OrderedDict([('name', <Parameter "name:str">), ('age', <Parameter "age:int">)])

返回的是一個有序字典

使用annotation將其轉爲class類型

params = inspect.signature(Person).parameters
for name,param in params.items():
    print(name,param.annotation)

返回以下:

name <class 'str'>
age <class 'int'>

經過類裝飾器判斷數據類型

新建一個類,作爲裝飾器判斷

class TypeAssert:
    def __init__(self,cls):
        self.cls = cls
    def __call__(self,name,age):
        params = inspect.signature(self.cls).parameters
        for name,param in params.items():
            print(name,param.annotation)
class Person:
    name = Typed(str)
    age = Typed(int)
def __init__(self,name:str,age:int):
    self.name = name
    self.age = age

將這兩句話去掉,不在類中調用Typed的類,將其在TypeAssert中進行調用並判斷

經過setattr 建立類的屬性,直接寫入到字典

name,age

class TypeAssert:
    def __init__(self,cls):
        self.cls = cls
    def __call__(self,name,age):
        params = inspect.signature(self.cls).parameters
        for name,param in params.items():
            print(name,param.annotation)
            if param.annotation != param.empty:        #判斷值是否有註解,將不等於空則加入類屬性
                setattr(self.cls,name,Typed(param.annotation))

這樣再添加屬性則被__set__攔截並修改

完整以下:

class Typed:
    def __init__(self,type):
        self.type = type
    def __get__(self, instance, owner):
        print('get :',self, instance, owner )
    def __set__(self, instance, value):
        print('set : ',self, instance, value)
        if not isinstance(value,self.type):
            raise ValueError(value)
class TypeAssert:
    def __init__(self,cls):
        self.cls = cls
    def __call__(self,name,age):
        params = inspect.signature(self.cls).parameters
        print(params)
        for name,param in params.items():
            print(name,param.annotation)
            if param.annotation != param.empty:
                setattr(self.cls,name,Typed(param.annotation))
@TypeAssert
class Person:
    name = Typed(str)
    age = Typed(int)
    def __init__(self,name:str,age:int):
        self.name = name
        self.age = age
p = Person('tom',11)
print(Person.__dict__)
print(type(p))
相關文章
相關標籤/搜索