将属性添加到python类

Rob*_*rto 5 python monkeypatching

请考虑以下代码:

class Foo():

    pass


Foo.entries = dict()


a = Foo()
a.entries['1'] = 1

b = Foo()
b.entries['3'] = 3

print(a.entries)
Run Code Online (Sandbox Code Playgroud)

这将打印:

{'1': 1, '3': 3}
Run Code Online (Sandbox Code Playgroud)

因为条目是作为静态属性添加的.有没有办法猴子修补类定义以添加新属性(不使用继承).

我设法找到了以下方式,但它看起来很复杂:

def patch_me(target, field, value):
    def func(self):
        if not hasattr(self, '__' + field):
            setattr(self, '__' + field, value())
        return getattr(self, '__' + field)
    setattr(target, field, property(func))

patch_me(Foo, 'entries', dict)
Run Code Online (Sandbox Code Playgroud)

Kev*_*vin 1

通常,属性由函数添加__init__()或在实例化后添加:

foo = Foo()
foo.bar = 'something'  # note case
Run Code Online (Sandbox Code Playgroud)

如果您想自动执行此操作,继承是迄今为止最简单的方法:

class Baz(Foo):
    def __init__(self):
        super().__init__()  # super() needs arguments in 2.x
        self.bar = 'something'
Run Code Online (Sandbox Code Playgroud)

请注意,类不需要出现在 Python 模块的顶层。您可以在函数内声明一个类:

def make_baz(value):
    class Baz(Foo):
        def __init__(self):
            super().__init__()  # super() needs arguments in 2.x
            self.bar = value()
    return Baz()
Run Code Online (Sandbox Code Playgroud)

此示例将在每次make_baz()调用时创建一个新类。这可能是也可能不是您想要的。这样做可能会更简单:

def make_foo(value):
    result = Foo()
    result.bar = value()
    return result
Run Code Online (Sandbox Code Playgroud)

如果您确实打算对原始类进行猴子修补,那么您提供的示例代码或多或少是最简单的方法。您可能会考虑使用装饰器语法property(),但这是一个很小的变化。我还应该注意,它不会调用双下划线名称修饰,这可能是一件好事,因为这意味着您不能与类本身使用的任何名称发生冲突。