如何将 python 类中的方法自动"register"为列表类变量?



在定义Python类时,我希望使用decorator将其一些方法注册到类变量列表中。下面是一个不正确的python示例,它概述了我正在寻找的内容:

class MyClass:
dangerous_methods = []
@classmethod
def dangerous_method(cls, func):
cls.dangerous_methods.append(func)
return func
@MyClass.dangerous_method
def incinerate(self):
pass
def watch_tv(self):
pass
@MyClass.dangerous_method
def stab(self):
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)

obj = MyClass()
obj.print_dangerous_methods()

预期输出为

[<function MyClass.incinerate at 0x000001A42A629280>, <function MyClass.stab at 0x000001A42A629281>]

有可能做到这一点而不过分折磨Python吗?

您真正想要做的就是在方法上设置dangerous。请记住,python函数和方法是一流的对象,您可以在它们上设置任意属性。

def print_dangerous_methods(cls):
""" yes, you could also return a list """
for name in dir(cls):
f = getattr(cls, name)
if callable(f) and getattr(f, "dangerous", False):
print(name)

def dangerous(func):
setattr(func, "dangerous", True)
return func
class MyClass:
@dangerous
def incinerate(self):
print("incinerate")
def watch_tv(self):
pass
@dangerous
def stab(self):
return "you've been stabbed"
class_born_dangerous = print_dangerous_methods
print("non instance")
obj = MyClass()
print_dangerous_methods(obj)
print("non class")
print_dangerous_methods(MyClass)
print("nand yes, they work")
obj.incinerate()
print (obj.stab())
print("nas a classmethod")
obj.class_born_dangerous()

输出:


on instance
incinerate
stab
on class
incinerate
stab
and yes, they work
incinerate
you've been stabbed
as a classmethod
incinerate
stab

如果你想推广这种方法并设置任意属性,你需要设置一个参数化的装饰器:

def annotate_func(**kwds):
"""set arbitrary attributes"""
def actual_decorator(func):
for k, v in kwds.items():
setattr(func, k, v)
return func
return actual_decorator

您将按如下方式使用:

@annotate_func(dangerous=1,range=1000)
def shoot(self, times):
for i in range(0, times):
print("bang!")

这是实现的一种方法

class MyClass:
def __init__(self):
self.dangerous_methods = []
def dangerous_method(func):
def inner(self):
self.dangerous_methods.append(func)
return func(self)
return inner
@dangerous_method
def incinerate(self):
print('Incinerate called!')
pass
def watch_tv(self):
print('Watch_tv called!')
pass
@dangerous_method
def stab(self):
print('Stab called!')
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)

obj = MyClass()
obj.incinerate()
# Incinerate called!
obj.watch_tv()
# Watch_tv called!
obj.stab()
# Stab called!
obj.incinerate()
# Incinerate called!
obj.print_dangerous_methods()
# [<function MyClass.incinerate at 0x0000029C11666EE8>, <function MyClass.stab at 0x0000029C11666B88>, <function MyClass.incinerate at 0x0000029C11666EE8>]

只需注意,通过这种方式,函数只有在调用后才能添加到列表中,并且存在函数多次添加到列表的风险。然而,如果你知道你想添加到列表中的一些函数是常量,你可以在构建对象时简单地添加它们:

class MyClass:
def __init__(self):
self.dangerous_methods = [self.incinerate, self.stab]
def incinerate(self):
print('Incinerate called!')
pass
def watch_tv(self):
print('Watch_tv called!')
pass
def stab(self):
print('Stab called!')
pass
def print_dangerous_methods(self):
print(self.dangerous_methods)

obj = MyClass()
obj.print_dangerous_methods()
# [<bound method MyClass.incinerate of <__main__.MyClass object at 0x0000029C11388F08>>, <bound method MyClass.stab of <__main__.MyClass object at 0x0000029C11388F08>>]

下面的代码片段正是您所描述的。

注意,print_dangerous_methods被声明为一个类方法,因为它实际上就是这样(它适用于类,而不是某个实例(。这意味着您甚至可以在不创建实例的情况下调用它。

class MyClass:
def dangerous_method(meth):
meth.is_dangerous = True
return meth
@dangerous_method
def incinerate(self):
pass
def watch_tv(self):
pass
@dangerous_method
def stab(self):
pass
@classmethod
def print_dangerous_methods(cls):
print ([
meth for meth in [
getattr(cls, methname) for methname in dir(cls)
]
if getattr(meth, "is_dangerous", False)
])
MyClass.print_dangerous_methods()

最新更新