Python元类默认属性
我试图在Python(2.7)中创建元类,该元类将设置作为对象属性传递给对象的Python元类默认属性,python,metaclass,Python,Metaclass,我试图在Python(2.7)中创建元类,该元类将设置作为对象属性传递给对象的\uuuu init\uuuu的参数 class AttributeInitType(type): def __call__(self, *args, **kwargs): obj = super(AttributeInitType, self).__call__(*args, **kwargs) for k, v in kwargs.items():
\uuuu init\uuuu
的参数
class AttributeInitType(type):
def __call__(self, *args, **kwargs):
obj = super(AttributeInitType, self).__call__(*args, **kwargs)
for k, v in kwargs.items():
setattr(obj, k, v)
return obj
用法:
class Human(object):
__metaclass__ = AttributeInitType
def __init__(self, height=160, age=0, eyes="brown", sex="male"):
pass
man = Human()
问题:我希望man
实例将默认属性设置为类的\uuuuu init\uuuu
中的属性。我怎么做
更新:我找到了更好的解决方案:
- 在类创建过程中只检查一次
方法\uuuuuuu init\uuuuu
- 不重写由类的real
\uuuuu init\uuuuu
import inspect
import copy
class AttributeInitType(type):
"""Converts keyword attributes of the init to object attributes"""
def __new__(mcs, name, bases, d):
# Cache __init__ defaults on a class-level
argspec = inspect.getargspec(d["__init__"])
init_defaults = dict(zip(argspec.args[-len(argspec.defaults):], argspec.defaults))
cls = super(AttributeInitType, mcs).__new__(mcs, name, bases, d)
cls.__init_defaults = init_defaults
return cls
def __call__(mcs, *args, **kwargs):
obj = super(AttributeInitType, mcs).__call__(*args, **kwargs)
the_kwargs = copy.copy(obj.__class__.__init_defaults)
the_kwargs.update(kwargs)
for k, v in the_kwargs.items():
# Don't override attributes set by real __init__
if not hasattr(obj, k):
setattr(obj, k, v)
return obj
如果在对象创建时传递参数,则情况会正常
>>> man
<test.Human object at 0x10a71e810>
>>> dir(man)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> man=Human(height=10)
>>> dir(man)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'height']
>>> man.height
10
男人
>>>主任(男)
“UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU","大小","str","子类hook","weakref"
>>>人=人(身高=10)
>>>主任(男)
“UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU","大小","结构","子类钩","高度",
>>>身高
10
但它不适用于默认参数。为此,您必须从\uuuu init\uuu
函数对象中专门提取它们
另一种方法是修饰
\uuuu init\uuuu
。您需要反思\uuuu init\uuuu
方法并从中提取任何默认值。在那里工作会有帮助的
函数返回参数名列表和默认值列表。您可以将这些信息结合起来,找到给定函数的默认参数规范,然后使用这些信息设置对象的属性:
import inspect
class AttributeInitType(type):
def __call__(self, *args, **kwargs):
obj = super(AttributeInitType, self).__call__(*args, **kwargs)
argspec = inspect.getargspec(obj.__init__)
defaults = dict(zip(argspec.args[-len(argspec.defaults):], argspec.defaults))
defaults.update(kwargs)
for key, val in defaults.items():
setattr(obj, key, val)
return obj
使用上述元类,您可以省略任何参数,它们将在新实例上设置,或者您可以通过显式传递它们来覆盖它们:
>>> man = Human()
>>> man.age
0
>>> man.height
160
>>> Human(height=180).height
180
在我的框架中,我使用*args/**kwargs、一个类默认字典以及对我的基本对象init中的属性设置器的调用来实现这一点。我觉得这比装饰更简单,比元类更简单
Class Base(object):
defaults = {"default_attribute" : "default_value"}
def __init__(self, *args, **kwargs):
super(Base, self).__init__()
self.attribute_setter(self.defaults.items(), *args, **kwargs)
def attribute_setter(self, *args, **kwargs):
if args: # also allow tuples of name/value pairs
for attribute_name, attribute_value in args:
setattr(self, attribute_name, attribute_value)
[setattr(self, name, value) for name, value in kwargs.items()]
b = Base(testing=True)
# print b.testing
# True
# print b.default_attribute
# "default_value"
通过将任意属性指定为关键字参数(或名称/值对的位置参数元组),这种组合允许在运行时通过init分配任意属性
类默认值字典用于提供默认参数,而不是init的参数列表中显式命名的关键字参数。这使得创建新实例时使用的默认属性在运行时可修改。您可以通过dict.copy+dict.update“继承”类词典。@ZaarHai:如果您有任何位置参数,它们将添加到
args
列表中,但不会添加到defaults
元组中。我明白了。谢谢如果你能在答案中加上这个注释,那就太好了。@ZaarHai:我链接到了argspec文档,其中详细介绍了这一点。