当我尝试这样的代码时:
def type_check(types):
def decorator(f):
def wrapper(self, v):
if not type(v) in types:
raise TypeError('Value is of type {} but allowed type(s): {}.'.format(type(v), types))
return f(self, v)
return wrapper
return decorator
class MyList:
@type_check(types=[list, MyList])
def __init__(self, value):
self.data = value[:]
# ... next are operator overloading and other unrelated to question code ...
我得到以下回溯输出:
MyList @type_check(types=[list, MyList]) NameError: name 'MyList' is not defined
我绕过了这一点,从MyListAnchor中派生MyList,并使用isinstance()检查类型,而不是严格的类型相等:
def type_check(types):
def decorator(f):
def wrapper(self, v):
if not any(filter(lambda t: isinstance(v, t), types)):
raise TypeError('Value is of type {} but must be an instance(s) of: {}.'.format(type(v), types))
return f(self, v)
return wrapper
return decorator
class MyListAnchor:
pass
class MyList(MyListAnchor):
@type_check(types=[list, MyListAnchor])
def __init__(self, value):
self.data = value[:]
# ... next are operator overloading and other unrelated to question code ...
这是最好的解决方案吗?如何将函数decorator添加到接受此类作为decorator调用参数之一的类方法中?
更新:我用类decorator:概括了decorator并解决了初始问题
"""
Type constrains format:
'method_name': (pargs, kargs)
where pargs - list of constrains for positional only arguments, like this
[[], [str, int], [Self]]
where empty list designate an absense of constrains and Self stands for class decorator
was added to.
Similarly kargs have format:
{'aname_0': [], 'aname_1': [str, int], 'aname_2': [Self]}
WARNING:
1) Positional only arguments in class methods constraned by this decorator must not
be passed by name (in keyworld-only args manner).
Having "def foo(x, y, z): ..." call it like "foo(1, 2, 3)" but not "foo(1, 2, z=3)"
or place constrains for "z" in either pargs or kargs. In either way you have to sacrifice
some flexibility in way you pass args in method and do not mixing args passing styles (choose
"foo(1, 2, 3)" or "foo(1, 2, z=3)")
2) When you call method with extra arguments (*pargs and **kwargs), extra arguments in *pargs and **kargs constrained
by pargs[-1] or kargs[None].
"""
class Self: pass
def type_check(ptypes, ktypes):
def decorator(f):
def wrapper(*pargs, **kargs):
i = 0
while i < len(pargs):
arg = pargs[i]
types = ptypes[i] if i < len(ptypes) else ptypes[-1]
if types and not type(arg) in types:
raise TypeError('Value is of type {} but allowed type(s): {}.'.format(type(arg), types))
i += 1
for k, v in kargs.items():
types = ktypes[k] if k in ktypes else ktypes[None]
if not type(v) in types:
raise TypeError('Value is of type {} but allowed type(s): {}.'.format(type(v), types))
return f(*pargs, **kargs)
return wrapper
return decorator
def methods_constrained(meth_cons: dict):
def decorator(c):
for meth, (ptypes, ktypes) in meth_cons.items():
for i in range(len(ptypes)):
ptypes[i] = [c if t == Self else t for t in ptypes[i]]
for k in ktypes:
ktypes[k] = [c if t == Self else t for t in ktypes[k]]
exec('c.{} = type_check(ptypes, ktypes)(c.__dict__[meth])'.format(meth))
return c
return decorator
使用示例:
import strict_types as st
@st.methods_constrained({
'__init__': ([[], [list, st.Self]], {}),
'foo': ([[], [dict, st.Self], [int, float], [str]], {'w': [st.Self], 'z': [float], 'qwerty': [bool], None: [bool]}),
})
class MyList:
def __init__(self, value):
self.data = value[:]
def foo(self, x, y=1, *pargs, w, z=2, **kargs):
print(self, x, y, pargs, w, z, kargs)
# ... next are operator overloading and other unrelated to question code ...
i = MyList([1, 2, 3])
i.foo({'key1': 'value'}, 3.14, 'a', 'b', 'c', 'd', w=i, z=0.1, qwerty=True, b0=False, b1=False, b2=True)
稍后我将添加从args注释中自动检索约束的信息(但在这种情况下,只允许使用一种类型)。此外,我没有考虑什么更好——严格的类型相等或isinstance()——或者两者都可以通过decorator的args选择相等模式(默认为strict)。
第页。S.:这个问题不是试图在python静态类型中重新发明轮子,而是关于python装饰器主题的一个实践。
您可以在定义类之后装饰__init__
:
class MyList:
def __init__(self, value):
self.data = value[:]
MyList.__init__ = type_check(types=[list, MyList])(MyList.__init__)