有没有办法让一个属性和方法具有相同的名称? 我的意思是可以通常使用和同时可以调用的属性? 像这样:
>>> b = Book()
>>> b.pages
123
>>> b.pages()
123
>>> b.pages(including_toc=False)
123
>>> b.pages(including_toc=True)
127
答案 0 :(得分:10)
不,你不能。
()
始终从左侧的表达式调用对象。
这意味着,b.pages()
可以理解如下:
_tmp = b.pages
_tmp()
如您所见,方法是属性。
你可能(但不应该)做的是在一些自定义类中包装整数并提供__call__
方法......但我建议不要使用这种黑魔法。
答案 1 :(得分:5)
Book
类的实例只能有一个名为pages
的属性。该属性可以是任何东西(整数,可调用函数,任何东西),但它只能是一个东西。
另一种查看事物的方法是在字节码级别 - 给出两行代码:
>>> 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
)
第二行(book.pages()
)执行完全相同的操作,加载book
对象,加载pages
属性,然后调用{{1属性。
没有理智的方法可以让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
添加单独的属性)
答案 2 :(得分:3)
简答:不,因为属性和方法是类的属性,并且是同一名称空间的一部分。 因此,稍后声明哪一个会覆盖前一个。
答案 3 :(得分:1)
我假设你的主要目标是让pages
在设置特定标志时返回不同的值。根据您的目标,可能有效的一种方法是使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
这并不是完全你要求的内容,但它对某个用例子集(即你将多次调用{的那些用例)一样好或更好{1}}设置了标记,偶尔只更改标记 - 例如最终用户设置pages
时,或者特定图书几乎总是或几乎从不包含include_toc
时它的页数。)
但是,正如phant0m指出的那样,该标志是持久的,因此在某些情况下这可能会产生意外结果,如果您设置它然后在完成后无法重置它。正如eryksun所指出的,上下文管理器是解决该问题的经典解决方案。
虽然这可能确实是过度工程,但它很简单,我仍然会展示它。只需将其添加到_toc
:
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