Python 描述器的应用
- 1、属性描述器的实现
- 2、类静态方法描述器实现
- 3、类方法描述器实现
- 4、参数检查
-
- 4.1 Version 1
- 4.2 Version 2
- 4.3 Version 3
1、属性描述器的实现
class Property:
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, instance, owner):
print(self, instance, owner)
return self.fget(instance)
def __set__(self, instance, value):
print('set ~~~~', self, instance, value)
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance, value)
def setter(self, fn):
self.fset = fn
return self
class A:
def __init__(self, x):
self._x = x
@Property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
a = A(10)
print(a.x)
a.x =100
print(a.x)
<__main__.Property object at 0x000002041BD051C0> <__main__.A object at 0x000002041BD05250> <class '__main__.A'>
10
set ~~~~ <__main__.Property object at 0x000002041BD051C0> <__main__.A object at 0x000002041BD05250> 100
<__main__.Property object at 0x000002041BD051C0> <__main__.A object at 0x000002041BD05250> <class '__main__.A'>
100
2、类静态方法描述器实现
class StaticMethod:
"""This is a StaticMethod class."""
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, owner):
return self.fn
class A:
""""Class A."""
@StaticMethod
def smtd(x, y):
"""Smtd in class A."""
return (x, y)
a = A()
print(a.smtd(4, 5))
print(a.smtd.__doc__, a.smtd.__name__)
(4, 5)
Smtd in class A. smtd
3、类方法描述器实现
from functools import partial, wraps, update_wrapper
class ClassMethod:
def __init__(self, fn):
self._fn = fn
def __get__(self, instance, owner):
newfunc = partial(self._fn, owner)
wraps(self._fn)(newfunc)
return newfunc
class A:
@ClassMethod
def clsmtd(cls, x, y):
return (x, y)
a = A()
print(a.clsmtd(2, 2))
print(A.clsmtd(2, 2))
print(a.clsmtd.__name__)
(2, 2)
(2, 2)
clsmtd
4、参数检查
4.1 Version 1
import inspect
class TypeCheck:
def __init__(self, typ):
self.type = typ
def __get__(self, instance, owner):
if instance:
return instance.__dict__[self.name]
else:
return self
def __set_name__(self, owner, name):
print(name, owner)
self.name = name
def __set__(self, instance, value):
if instance:
if isinstance(value, self.type):
instance.__dict__[self.name] = value
else:
raise TypeError(self.name)
def datainject(cls):
sig = inspect.signature(cls)
params = sig.parameters
for name, param in params.items():
print(name, param.name, param.kind, param.default, param.annotation)
if param.annotation != inspect._empty:
setattr(cls, name, TypeCheck(param.annotation))
return cls
@datainject
class Person:
def __init__(self, name:str, age:int):
self.name = name
self.age = age
tom = Person('tom', 20)
print(tom.name)
print(tom.age)
print(Person.name)
name name POSITIONAL_OR_KEYWORD <class 'inspect._empty'> <class 'str'>
age age POSITIONAL_OR_KEYWORD <class 'inspect._empty'> <class 'int'>
AttributeError: 'TypeCheck' object has no attribute 'name'
4.2 Version 2
import inspect
class TypeCheck:
def __init__(self, name, typ):
self.type = typ
self.name =name
def __get__(self, instance, owner):
if instance:
return instance.__dict__[self.name]
else:
return self
def __set__(self, instance, value):
if instance:
if isinstance(value, self.type):
instance.__dict__[self.name] = value
else:
raise TypeError(self.name)
def datainject(cls):
sig = inspect.signature(cls)
params = sig.parameters
for name, param in params.items():
print(name, param.name, param.kind, param.default, param.annotation)
if param.annotation != inspect._empty:
setattr(cls, name, TypeCheck(name, param.annotation))
return cls
@datainject
class Person:
def __init__(self, name:str, age:int):
self.name = name
self.age = age
tom = Person('tom', 20)
print(tom.name)
print(tom.age)
print(Person.name)
name name POSITIONAL_OR_KEYWORD <class 'inspect._empty'> <class 'str'>
age age POSITIONAL_OR_KEYWORD <class 'inspect._empty'> <class 'int'>
tom
20
<__main__.TypeCheck object at 0x000002041BCF9A60>
4.3 Version 3
import inspect
from functools import wraps, update_wrapper
class TypeCheck:
def __init__(self, name, typ):
self.type = typ
self.name =name
def __get__(self, instance, owner):
if instance:
return instance.__dict__[self.name]
else:
return self
def __set__(self, instance, value):
if instance:
if isinstance(value, self.type):
instance.__dict__[self.name] = value
else:
raise TypeError(self.name)
class DataInject:
def __init__(self, cls):
self.cls = cls
sig = inspect.signature(cls)
params = sig.parameters
for name, param in params.items():
if param.annotation != inspect._empty:
setattr(cls, name, TypeCheck(name, param.annotation))
def __call__(self, *args, **kwargs):
return self.cls(*args, **kwargs)
@DataInject
class Person:
def __init__(self, name:str, age:int):
self.name = name
self.age = age
tom = Person('tom', 20)
print(tom.name)
print(tom.age)
print(Person.__dict__)
tom
20
{'cls': <class '__main__.Person'>}