同じ名前のプロパティとメソッドを持つ方法はありますか?通常の方法で使用でき、同時に呼び出すことができるプロパティを意味しますか?このような:
>>> b = Book()
>>> b.pages
123
>>> b.pages()
123
>>> b.pages(including_toc=False)
123
>>> b.pages(including_toc=True)
127
同じ名前のプロパティとメソッドを持つ方法はありますか?通常の方法で使用でき、同時に呼び出すことができるプロパティを意味しますか?このような:
>>> b = Book()
>>> b.pages
123
>>> b.pages()
123
>>> b.pages(including_toc=False)
123
>>> b.pages(including_toc=True)
127
いいえ、できません。
()
常に左側の式からオブジェクトを呼び出します。
これが意味することは、b.pages()
次のように読むことができるということです。
_tmp = b.pages
_tmp()
ご覧のとおり、メソッドは属性です。
あなたができること(しかしすべきではないこと)は、いくつかのカスタムクラスで整数をラップし、__call__
メソッドを提供することです…しかし、私はそのような黒魔術に反対することをお勧めします。
クラスのインスタンスは、Book
と呼ばれる属性を1つだけ持つことができますpages
。その属性は何でもかまいません(整数、呼び出し可能な関数、実際には何でも)が、それは1つだけです。
物事を表示する別の方法は、バイトコードレベルです-2行のコードが与えられます:
>>> def a():
... book.pages
... book.pages()
...
これがそれdisassembles
です:
>>> dis.dis(a)
2 0 LOAD_GLOBAL 0 (book)
3 LOAD_ATTR 1 (pages)
6 POP_TOP
3 7 LOAD_GLOBAL 2 (book)
10 LOAD_ATTR 1 (pages)
13 CALL_FUNCTION 0
[...a few more irrelevant lines...]
最初の行(book.pages
)はbook
オブジェクトをロードし、そこからpages
属性(LOAD_ATTR
)をロードします
2行目(book.pages()
)はまったく同じことを行い、book
オブジェクトをロードし、属性をロードしてから、pages
属性を呼び出しCALL_FUNCTION
ます。
LOAD_ATTR
最終的にどのように使用されるかに基づいて、返品を別のものにすることができる正しい方法はありません。あなたが得ることができる最も近いものは、両方のように振る舞う奇妙なオブジェクトを返すことです
>>> class WeirdInteger(int):
... def __call__(self, including_toc):
... print "WeirdInteger(%s) called" % (self)
...
>>> a = WeirdInteger(10)
>>> a
10
>>> a*2
20
>>> a()
WeirdInteger(10) called
..しかし、そうしないでください。あなたのコードを使用している人は誰もpages
属性がこのように機能することを期待しません、そして渡されるコードのビットはpages
実際の整数を必要とするかもしれません。
代わりに、Books
クラスを別の方法で設計します(pages
通常の関数を作成するか、別のプロパティを追加しますpages_without_toc
)
簡単な回答:いいえ。プロパティとメソッドはクラスの属性であり、同じ名前空間の一部であるためです。したがって、後で宣言されるものは前のものをオーバーライドします。
特定のフラグが設定されている場合、あなたの主な目標はpages
異なる値を返すことだと思います。目標に応じて機能する可能性のあるアプローチの1つはpages
、属性ではなくプロパティ(正確にはPythonの意味で)を作成することです。toc
次に、フラグが設定されているかどうかに応じて、値の有無に応じて値を返すようにします。したがって、たとえば:
class Book(object):
def __init__(self, toc, pages):
self._toc = toc
self._pages = pages
self.include_toc = False
@property
def pages(self):
if self.include_toc:
return self._pages + self._toc
else:
return self._pages
仕組みは次のとおりです。
>>> b = Book(5, 55)
>>> b.pages
55
>>> b.include_toc = True
>>> b.pages
60
これはあなたが求めていたものを正確に実行するわけではありませんが、特定のユースケースのサブセット(つまりpages
、フラグを設定して複数の呼び出しを行い、フラグを時々変更するだけの場合)には同じかそれ以上です- -エンドユーザーが設定する場合や、特定の書籍のページ数にinclude_toc
ほとんどの場合またはほとんど含まない場合など。)_toc
ただし、phant0mが指摘しているように、フラグは永続的であるため、設定した後でリセットに失敗すると、予期しない結果が生じる場合があります。そしてeryksunが指摘しているように、コンテキストマネージャーはその問題に対する古典的な解決策です。
これは確かに過剰設計かもしれませんが、それでもそれを実証するほど単純です。Book
これを:の定義に追加するだけです。
@contextlib.contextmanager
def set_toc_reset(self, state):
try:
old_flag = self.include_toc
self.include_toc = state
yield self
finally:
self.include_toc = old_flag
これにより、フラグがリセットされます。
>>> from foo import Book
>>> b = Book(5, 55)
>>> with b.set_toc_reset(True):
... print b.pages
...
60
>>> b.include_toc
False