3

__init__オブジェクトに渡される引数をオブジェクト属性として設定するメタクラスをPython(2.7)で作成しようとしています。

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インスタンスにクラスののようにデフォルトの属性を設定させたいのですが__init__。どうすればいいですか?

更新:私はさらに良い解決策に到達しました:

  • __init__クラスの作成中にメソッドを1回だけ検査します
  • クラスの実数によって(おそらく)設定された属性をオーバーライドしません__init__

コードは次のとおりです。

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
4

3 に答える 3

3

メソッドをイントロスペクトし、__init__そこからデフォルト値を抽出する必要があります。このgetargspec関数はそこで役立ちます。

このgetargspec関数は、(とりわけ)引数名のリストとデフォルト値のリストを返します。これらを組み合わせて、特定の関数のデフォルトの引数仕様を見つけ、その情報を使用してオブジェクトに属性を設定できます。

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
于 2012-08-20T11:10:33.360 に答える
0

オブジェクトの作成時に引数を渡すと、状況はうまくいきます

>>> 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

ただし、デフォルトの引数では機能しません。そのためには、__init__関数オブジェクトからそれらを具体的に抽出する必要があります。

別の方法は、代わりに装飾する__init__ことです。

于 2012-08-20T11:09:04.957 に答える
0

これは、フレームワークで* args / ** kwargs、クラスのデフォルトディクショナリ、およびベースオブジェクトinitのattribute_setterの呼び出しを使用して行います。これは装飾よりも単純で、メタクラスよりも明らかに複雑ではないように感じます。

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を介してクラスディクショナリを「継承」できます。

于 2014-09-14T00:19:24.063 に答える