Python动态设置非实例类属性
我想动态地添加类的属性,但不是在实例级别上。比如,我可以手动这样做:
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