python动态设置非实例类属性

时间:2011-12-13 14:20:02

标签: python attributes metaprogramming

我试图动态添加类属性,但不是在实例级别。例如。我可以手动做什么:

class Foo(object):
    a = 1
    b = 2
    c = 3

我希望能够做到:

class Foo(object):
    dct = {'a' : 1, 'b' : 2, 'c' : 3}
    for key, val in dct.items():
        <update the Foo namespace here>

我希望能够在没有从类外部调用类(因此它是可移植的)或没有其他类/装饰器的情况下执行此操作。这可能吗?

3 个答案:

答案 0 :(得分:4)

从您的示例代码判断,您希望在创建类的同时执行此操作。在这种情况下,假设您正在使用CPython,则可以使用locals()

class Foo(object):
    locals().update(a=1, b=2, c=3)

这是有效的,因为在定义类时,locals()引用类名称空间。它是特定于实现的行为,可能无法在更高版本的Python或替代实现中使用。

使用类工厂的不太脏的hacky版本如下所示。基本思想是通过type()构造函数将字典转换为类,然后将其用作新类的基类。为了方便用最少的语法定义属性,我使用**约定来接受属性。

def dicty(*bases, **attrs):
    if not bases:
        bases = (object,)
    return type("<from dict>", bases, attrs)

class Foo(dicty(a=1, b=2, c=3)):
    pass

# if you already have the dict, use unpacking

dct = dict(a=1, b=2, c=3)

class Foo(dicty(**dct)):
    pass

这实际上只是自己调用type()的语法糖。这很好,例如:

 class Foo(type("<none>", (object,), dict(a=1, b=2, c=3))):
     pass

答案 1 :(得分:2)

你的意思是这样的:

def update(obj, dct):
    for key, val in dct.items():
        obj.setattr(key, val)

然后去吧

update(Foo, {'a': 1, 'b': 2, 'c': 3})

这很有效,因为类也只是一个对象;)

如果你想将所有内容都移到课堂上,那么试试这个:

class Foo(object):
    __metaclass__ = lambda t, p, a: return type(t, p, a['dct'])
    dct = {'a': 1, 'b': 2, 'c': 3}

这将创建一个新类,成员位于dct,但所有其他属性都不会出现 - so ,您想要将最后一个参数更改为{{1}包括你想要的东西。我在这里找到了如何做到这一点:What is a metaclass in Python?

答案 2 :(得分:1)

接受的答案是一个很好的方法。但是,一个缺点是你最终在MRO继承链中有一个额外的父对象,这个对象实际上并不是必需的,甚至可能会让人感到困惑:

>>> Foo.__mro__
(<class '__main__.Foo'>, <class '__main__.<from dict>'>, <class 'object'>)

另一种方法是使用装饰器。像这样:

def dicty(**attrs):
    def decorator(cls):
        vars(cls).update(**attrs)
        return cls
    return decorator

@dicty(**some_class_attr_namespace)
class Foo():
    pass

通过这种方式,您可以避免继承链中的其他对象。 @decorator语法只是一种很好的说法:

Foo = dicty(a=1, b=2, c=3)(Foo)
相关问题