Python动态设置非实例类属性

3 投票
3 回答
628 浏览
提问于 2025-04-17 08:17

我想动态地添加类的属性,但不是在实例级别上。比如,我可以手动这样做:

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 个回答

2

被接受的答案是一种不错的方法。不过,它有一个缺点,就是在继承链中多了一个不必要的父对象,这可能会让人感到困惑:

>>> 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)
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中的成员,但其他的属性就不会有了——所以,你需要修改type的最后一个参数,来包含你想要的内容。我在这里找到了怎么做的:Python中的元类是什么?

5

根据你的示例代码,你想在创建类的同时做一些事情。在这种情况下,如果你使用的是CPython,可以使用 locals()

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

之所以这样做是因为在定义类的时候,locals() 会指向这个类的命名空间。这是特定实现的行为,可能在以后的Python版本或其他实现中不再适用。

下面展示了一种不那么“黑科技”的方法,使用了类工厂。基本的想法是通过 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

撰写回答