9

有没有办法拥有同名的属性和方法?我的意思是一个可以按通常方式使用并且可以同时调用的属性?像这样:

>>> b = Book()
>>> b.pages
123
>>> b.pages()
123
>>> b.pages(including_toc=False)
123
>>> b.pages(including_toc=True)
127
4

4 回答 4

10

不,你不能。

()总是从其左侧的表达式中调用一个对象。

这意味着,b.pages()可以如下解读:

_tmp = b.pages
_tmp()

如您所见,方法属性。

可以(但不应该)做的是将整数包装在一些自定义类中并提供一种__call__方法……但我建议不要使用这种黑魔法。

于 2012-10-23T11:27:09.477 回答
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属性,然后调用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.

于 2012-10-23T11:51:45.130 回答
3

简短回答:不,因为属性和方法是类的属性,并且属于同一个命名空间。因此,稍后声明的会覆盖前一个。

于 2012-10-23T11:28:35.720 回答
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

这并不完全符合您的要求,但对于某些用例子集(即您将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
于 2012-10-23T12:45:41.823 回答