[英]How to automatically "register" methods in a python class as a list class variable?
在定義 Python class 時,我想使用裝飾器將其一些方法注冊到 class 變量列表中。 這是一個不正確的 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()
預期的 output 是
[<function MyClass.incinerate at 0x000001A42A629280>, <function MyClass.stab at 0x000001A42A629281>]
是否可以在不過度折磨 Python 的情況下做到這一點?
這是實現它的一種方法:
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>]
請注意,通過這種方式,只有在調用函數后才會將函數添加到列表中,並且存在將 function 多次添加到列表中的風險。 但是,如果您知道要添加到列表中的某些函數並且它們是常量,則可以在構建 object 時簡單地添加它們:
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>>]
您真正想要做的就是在方法上設置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!")
以下代碼段完全符合您的描述。
請注意, print_dangerous_methods
被聲明為 class 方法,因為這就是它的真正含義(它適用於 class,而不適用於某個實例)。 這意味着即使不創建實例也可以調用它。
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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.