Python 方法重载装饰器
我正在尝试编写一个decorator,为python提供方法重载功能,类似于中提到的 我编写的decorator对常规函数非常有效,但我无法使它对类中的方法有效 这是装饰师:Python 方法重载装饰器,python,decorator,Python,Decorator,我正在尝试编写一个decorator,为python提供方法重载功能,类似于中提到的 我编写的decorator对常规函数非常有效,但我无法使它对类中的方法有效 这是装饰师: class Overload(object): def __init__(self, default): self.default_function = default self.type_map = {} self.pos = None def __cal
class Overload(object):
def __init__(self, default):
self.default_function = default
self.type_map = {}
self.pos = None
def __call__(self, *args, **kwargs):
print self
try:
if self.pos is None:
pos = kwargs.get("pos", 0)
else:
pos = self.pos
print args, kwargs
return self.type_map[type(args[pos])](*args, **kwargs)
except KeyError:
return self.default_function(*args, **kwargs)
except IndexError:
return self.default_function(*args, **kwargs)
def overload(self, *d_type):
def wrapper(f):
for dt in d_type:
self.type_map[dt] = f
return self
return wrapper
当我尝试这样实现它时:
class MyClass(object):
def __init__(self):
self.some_instance_var = 1
@Overload
def print_first_item(self, x):
return x[0], self.some_instance_var
@print_first_item.overload(str)
def print_first_item(self, x):
return x.split()[0], self.some_instance_var
当我运行它时,我得到一个TypeError
:
>>> m = MyClass()
>>> m.print_first_item(1)
<__main__.Overload object at 0x2> (1,) {}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "overload.py", line 17, in __call__
return self.default_function(*args, **kwargs)
TypeError: print_first_item() takes exactly 2 arguments (1 given)
>>>
>m=MyClass()
>>>m.打印第一项(1)
(1,) {}
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
调用中第17行的文件“重载.py”__
返回self.default_函数(*args,**kwargs)
TypeError:print\u first\u item()正好接受2个参数(给定1个)
>>>
我的问题是:如何从装饰方法中访问
MyClass
(即self
)的实例?正如abarnert所说,当您使用类时,装饰器“self”是重载的实例,而不是您希望/期望的MyClass
我找不到一个简单的解决办法。我能想到的最好的办法是不要使用类作为装饰器,而是使用函数,而是使用第二个参数,默认为字典。因为这是一个可变类型,所以每次调用函数时它都是同一个字典。我用它来存储我的“类变量”。其余部分采用与您的解决方案类似的模式
例如:
import inspect
def overload(funcOrType, map={}, type=None):
if not inspect.isclass(funcOrType):
# We have a function so we are dealing with "@overload"
if(type):
map[type] = funcOrType
else:
map['default_function'] = funcOrType
else:
def overloadWithType(func):
return overload(func, map, funcOrType)
return overloadWithType
def doOverload(*args, **kwargs):
for type in [t for t in map.keys() if t != 'default_function'] :
if isinstance(args[1], type): # Note args[0] is 'self' i.e. MyClass instance.
return map[type](*args, **kwargs)
return map['default_function'](*args, **kwargs)
return doOverload
然后:
Yeilds:
('a', 1)
('One', 1)
本质上,您的
重载
类需要一个\uuu获取
方法:
def __get__(self, obj, cls):
# Called on access of MyClass.print_first_item.
# We return a wrapper which calls our
print "get", self, obj, cls
if obj is None:
# a function would do some checks here, but we leave that.
return self
else:
return lambda *a, **k: self(obj, *a, **k)
为什么?
好吧,您使用重载对象作为一种函数替换。您希望它像函数一样,在具有不同签名的方法上下文中表示自己
简要说明方法访问的工作原理:
object.meth(1, 2)
被翻译成
object.__dict__['meth'].__get__(object, type(object))(1, 2)
函数的\uuuu get\uuuu()
返回一个方法对象,该方法对象通过将对象前置到参数列表中来包装函数(在参数列表中它会导致self
):
其中,realmethod
是一个方法对象,它知道要调用的函数和给它的self
,并通过将调用转换为
meth(object, 1, 2)
我们在这个新的\uuuu get\uuu
方法中模拟了这种行为。由于GLGL的详细解释,以下是工作实现,以供参考:
argtype_tuple = lambda args: tuple(type(a) for a in args)
class Overload(object):
def __init__(self, func):
self.default = func
self.map = {}
def __call__(self, *args, **kwargs):
key_tuple = argtype_tuple(args)
c_inst = kwargs.pop("c_inst", None)
if c_inst:
args = (c_inst,) + args
try:
return self.map[key_tuple](*args, **kwargs)
except KeyError:
return self.default(*args, **kwargs)
def __get__(self, obj, cls):
if obj:
return lambda *args, **kwargs: self(c_inst=obj, *args, **kwargs)
else:
return self
def overload(self, *types):
def wrapper(f):
for type_seq in types:
if type(type_seq) == tuple:
type_seq = tuple(type_seq)
else:
type_seq = (type_seq,)
self.map[type_seq] = f
return self
return wrapper
#Some tests/usage examples
class A(object):
@Overload
def print_first(self, x):
return x[0]
@print_first.overload(str)
def p_first(self, x):
return x.split()[0]
def __repr__(self):
return "class A Instance"
a = A()
assert a.print_first([1,2,3]) == 1
assert a.print_first("one two three") == "one"
@Overload
def flatten(seq):
return [seq]
@flatten.overload(list, tuple)
def flat(seq):
return sum((flatten(item) for item in seq), [])
assert flatten([1,2,[3,4]]) == [1,2,3,4]
assert flat([1,2,[3,4]]) == [1,2,3,4]
你有没有看过PEAK.Rules中的参考实现,或者pje早期的PEP和list帖子附带的十几个其他参考实现中的任何一个?如果你想实际使用这一点,而不是试图探索Python,那么使用他的作品(至少有几个人已经使用和测试过)可能比重复它更有意义。@abarnert:我没有意识到这一点。谢谢你的提醒。话虽如此,我真的只是想知道为什么我的实现没有像预期的那样工作,以及如何修复它。我是,正如你所说的“探索Python”。首先,你知道@functools.wrapps等吗。?他们会让你的生活轻松很多,但这对你没有帮助。无论如何,这里问题的第一部分是,默认的\u函数被替换为一个类似于类的函数,它不是一个方法(重载。调用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。但很明显,你不能只做(self,realself,*args,**kwargs)调用(call),然后期望它起作用。今晚我没有时间细说;希望在我回来之前能有人帮忙。太棒了。根据你的建议,我成功了+1以获取查找工作原理的详细说明。
meth(object, 1, 2)
argtype_tuple = lambda args: tuple(type(a) for a in args)
class Overload(object):
def __init__(self, func):
self.default = func
self.map = {}
def __call__(self, *args, **kwargs):
key_tuple = argtype_tuple(args)
c_inst = kwargs.pop("c_inst", None)
if c_inst:
args = (c_inst,) + args
try:
return self.map[key_tuple](*args, **kwargs)
except KeyError:
return self.default(*args, **kwargs)
def __get__(self, obj, cls):
if obj:
return lambda *args, **kwargs: self(c_inst=obj, *args, **kwargs)
else:
return self
def overload(self, *types):
def wrapper(f):
for type_seq in types:
if type(type_seq) == tuple:
type_seq = tuple(type_seq)
else:
type_seq = (type_seq,)
self.map[type_seq] = f
return self
return wrapper
#Some tests/usage examples
class A(object):
@Overload
def print_first(self, x):
return x[0]
@print_first.overload(str)
def p_first(self, x):
return x.split()[0]
def __repr__(self):
return "class A Instance"
a = A()
assert a.print_first([1,2,3]) == 1
assert a.print_first("one two three") == "one"
@Overload
def flatten(seq):
return [seq]
@flatten.overload(list, tuple)
def flat(seq):
return sum((flatten(item) for item in seq), [])
assert flatten([1,2,[3,4]]) == [1,2,3,4]
assert flat([1,2,[3,4]]) == [1,2,3,4]