10

Is there any way to have a property and a method with the same name? I mean a property that can be used the usual way and to be callable at the same time? Like this:

>>> b = Book()
>>> b.pages
123
>>> b.pages()
123
>>> b.pages(including_toc=False)
123
>>> b.pages(including_toc=True)
127
Zahory
  • 103
  • 1
  • 4
  • 1
    In case you are coming from PHP where this is directly supported by the language, see [here for some similar differences](http://stackoverflow.com/a/11901500/383124) where I also mention the attributes vs methods distinction that PHP makes but Python doesn't. P.S. Welcome to StackOverflow! – phant0m Oct 23 '12 at 11:48

4 Answers4

10

No, you can't.

() always calls an object from the expression on its left-hand side.

What this means is, that b.pages() can be read as follows:

_tmp = b.pages
_tmp()

As you can see, methods are attributes.

What you could (but shouldn't) do is wrap integers in some custom class and provide a __call__ method… but I would advise against such black magic.

phant0m
  • 16,595
  • 5
  • 50
  • 82
5

A instance of your Book class can only have one attribute called pages. That attribute can be anything (an integer, a callable function, anything really), but it can only be one thing.

Another way of viewing things is at the byte-code level - given two lines of code:

>>> def a():
...     book.pages
...     book.pages()
... 

Here is what it 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...]

The first line (book.pages) loads the book object, and from that loads the pages attribute (LOAD_ATTR)

The second line (book.pages()) does the exact same thing, loads the book object, loads the pages attribute, then calls the CALL_FUNCTION attribute.

There is no sane way you can make the LOAD_ATTR return something different based on how it will eventually be used. The closest you can get is to return a weird object which acts like both

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

..but, don't do that. No one using you code will expect the pages attribute to work like this, and bits of code the pages will be passed to might require an actual integer.

Instead design your Books class differently (maybe make pages an regular function, or add a separate property for the pages_without_toc)

dbr
  • 165,801
  • 69
  • 278
  • 343
3

Short Answer: No, as properties and methods are attributes of a class and are part of the same namespace. So, which one gets declared later overrides the previous one.

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • 1
    I would advise against the wording `share the same namespace`, since methods effectively *are* attributes, that happen to be callable. – phant0m Oct 23 '12 at 11:33
1

I am assuming that your main goal is to have pages return a different value if a particular flag is set. One approach that might work, depending on your goal, would be to make pages a property (in the precise Python sense) rather than an attribute. Then have it return a value with toc or without, depending on whether a flag is set. So for example:

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

Here's how it would work:

>>> b = Book(5, 55)
>>> b.pages
55
>>> b.include_toc = True
>>> b.pages
60

This doesn't do exactly what you've asked for, but it is as good or better for a certain subset of use cases (i.e. those in which you will be making multiple calls to pages with the flag set, only changing the flag occasionally -- such as when include_toc is set by an end user, or when a particular book should almost always or almost never include the _toc in its page count.)

However, as phant0m points out, the flag is persistent, so this could generate unexpected results in some cases, if you set it and then fail to reset it when you're done. And as eryksun points out, a context manager is a classic solution to that problem.

While this may indeed be overengineering, it's so simple that I'll demonstrate it nonetheless. Simply add this to the definition of 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

This takes care of resetting the flag for you:

>>> from foo import Book
>>> b = Book(5, 55)
>>> with b.set_toc_reset(True):
...     print b.pages
... 
60
>>> b.include_toc
False
senderle
  • 145,869
  • 36
  • 209
  • 233
  • 1
    Note that the converse is true as well, remember not to forget setting `include_toc` before accessing the `pages`, or you can get unexpected results. For instance, you might have added some code further above that needed a different setting, but forgot to revert its status. – phant0m Oct 23 '12 at 13:38
  • 1
    You could make this a context manager, such as `with b.include_toc(): pages = b.pages`. The method `include_toc` could set `self._include_toc = True`, and `__exit__` could reset it. But this is over-engineering this problem compared to simply having a separate property. – Eryk Sun Oct 23 '12 at 14:09
  • @eryksun, you're right that it probably is over-engineering in many cases. But still worth considering, I think, given how easy it is to create a context manager. – senderle Oct 23 '12 at 16:06