簡體   English   中英

如何將 python class 中的方法自動“注冊”為列表 class 變量?

[英]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()

output:


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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM