2

Pythonでは、属性に直接割り当てるか、属性の状態を変更するメソッド呼び出しを行うことで、インスタンスの状態を変更できます。

foo.thing = 'baz'

また:

foo.thing('baz')

このように動作する多数の属性にスケーリングする上記の両方の形式を受け入れるクラスを作成するための優れた方法はありますか?(簡単に、私が特に気に入らない実装の例を示します。)これがばかげたAPIだと思っているなら、私に知らせてください。しかし、おそらくもっと具体的な例が必要です。Documentクラス があるとしましょう。Document属性を持つことができますtitle。ただし、titleいくつかの状態(font、fontsize、justification、...)も必要な場合がありますが、平均的なユーザーは、タイトルを文字列に設定してそれを実行するだけで十分満足できる場合があります...

これを実現する1つの方法は、次のとおりです。

class Title(object):
     def __init__(self,text,font='times',size=12):
         self.text = text
         self.font = font
         self.size = size
     def __call__(self,*text,**kwargs):
         if(text):
             self.text = text[0]
         for k,v in kwargs.items():
             setattr(self,k,v)
     def __str__(self):
         return '<title font={font}, size={size}>{text}</title>'.format(text=self.text,size=self.size,font=self.font)

class Document(object):
     _special_attr = set(['title'])
     def __setattr__(self,k,v):
         if k in self._special_attr and hasattr(self,k):
             getattr(self,k)(v)
         else:
             object.__setattr__(self,k,v)

     def __init__(self,text="",title=""):
         self.title = Title(title)
         self.text = text

     def __str__(self):
         return str(self.title)+'<body>'+self.text+'</body>'

これで、これを次のように使用できます。

doc = Document()
doc.title = "Hello World"
print (str(doc))
doc.title("Goodbye World",font="Helvetica")
print (str(doc))

ただし、この実装は少し厄介なようです(with __special_attr)。たぶん、これはめちゃくちゃなAPIだからです。わからない。 これを行うためのより良い方法はありますか? それとも、私はこれに少し行き過ぎた道を残しましたか?

これにも使用できることは@propertyわかっていますが、このように動作する属性が複数ある場合は、まったく拡張できません。それぞれにゲッターとセッターを作成する必要があります

4

2 に答える 2

4

以前の回答が想定しているよりも少し難しいです。

記述子に格納されている値はすべてのインスタンス間で共有されるため、インスタンスごとのデータを格納するのに適切な場所ではありません。また、obj.attrib(...)2つのステップで実行されます。

tmp = obj.attrib
tmp(...)

Pythonは、2番目のステップが続くことを事前に認識していないため、呼び出し可能で、そのオブジェクトへの参照を持つものを常に返す必要があります。

次の例では、その参照がset引数に含まれています。

class CallableString(str):
    def __new__(class_, set, value):
        inst = str.__new__(class_, value)
        inst._set = set
        return inst
    def __call__(self, value):
        self._set(value)

class A(object):
    def __init__(self):
        self._attrib = "foo"
    def get_attrib(self):
        return CallableString(self.set_attrib, self._attrib)
    def set_attrib(self, value):
        try:
            value = value._value
        except AttributeError:
            pass
        self._attrib = value
    attrib = property(get_attrib, set_attrib)

a = A()
print a.attrib
a.attrib = "bar"
print a.attrib
a.attrib("baz")
print a.attrib

要するに、あなたが望むことは透過的に行うことはできません。この制限をハッキングすることを主張しないのであれば、より良いPythonコードを書くことができます

于 2012-09-20T06:49:29.683 に答える
3

適切なルールに従う記述子クラスを作成するだけで@property、潜在的に数百の属性を使用する必要がなくなります。

# Warning: Untested code ahead
class DocAttribute(object):
    tag_str = "<{tag}{attrs}>{text}</{tag}>"

    def __init__(self, tag_name, default_attrs=None):
        self._tag_name = tag_name
        self._attrs = default_attrs if default_attrs is not None else {}

    def __call__(self, *text, **attrs):
        self._text = "".join(text)
        self._attrs.update(attrs)
        return self

    def __get__(self, instance, cls):
        return self

    def __set__(self, instance, value):
        self._text = value

    def __str__(self):
        # Attrs left as an exercise for the reader
        return self.tag_str.format(tag=self._tag_name, text=self._text)

Document次に、のメソッドを使用__setattr__して、承認された名前のホワイトリストにある場合(またはドメインによっては禁止されている名前のブラックリストにない場合)、このクラスに基づいて記述子を追加できます。

class Document(object):
    # prelude
    def __setattr__(self, name, value):
        if self.is_allowed(name):  # Again, left as an exercise for the reader
            object.__setattr__(self, name, DocAttribute(name)(value))
于 2012-09-20T03:18:06.797 に答える