Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: predicate methods as properties?

Tags:

python

pep8

By using the @property decorator, Python has completely eliminated the need for getters and setters on object properties (some might say 'attributes'). This makes code much simpler, while maintaining the extensibility when things do need to get more complex.

I was wondering what the Pythonic approach to the following kind of method is, though. Say I have the following class:

class A(object):
    def is_winner(self):
        return True  # typically a more arcane method to determine the answer

Such methods typically take no arguments, and have no side effects. One might call these predicates. And given their name, they often closely resemble something one might also have stored as a property.

I am inclined to add a @property decorator to the above, in order to be able to call it as an object property (i.e. foo.is_winner), but I was wondering if this is the standard thing to do. At first glance, I could not find any documentation on this subject. Is there a common standard for this situation?

like image 929
Joost Avatar asked Apr 10 '15 18:04

Joost


People also ask

What is Property method in Python?

The property() method in Python provides an interface to instance attributes. It encapsulates instance attributes and provides a property, same as Java and C#. The property() method takes the get, set and delete methods as arguments and returns an object of the property class.

How can you see which properties and methods are available for a specific Python object?

The simplest way to get a list of methods of any object is to use the help() command. It will list out all the available/important methods associated with that object.

What is __ get __ in Python?

__get__(self, obj, type=None) : This attribute is called when you want to retrieve the information (value = obj. attr) , and whatever it returns is what will be given to the code that requested the attribute's value. gfg.

How do I get the properties of a Python object?

One of the easiest ways to access a Python object's attributes is the dir() function. This function is built-in directly into Python, so there's no need to import any libraries.


1 Answers

It seems that the general consensus is that attributes are generally seen as being instant and next-to-free to use, so if the computation being decorated as a @property is expensive, it's probably best to either cache the outcome for repeated use (@Martijn Pieters) or to leave it as a method, as methods are generally expected to take more time than attribute lookups. PEP 8 notes specifically:

  • Note 2: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

  • Note 3: Avoid using properties for computationally expensive operations; the attribute notation makes the caller believe that access is (relatively) cheap.


One particular use case of the @property decorator is to add some behavior to a class without requiring that users of the class change from foo.bar references to foo.bar() calls -- for example, if you wanted to count the number of times that an attribute was referenced, you could convert the attribute into a @property where the decorated method manipulates some state before returning the requested data.

Here is an example of the original class:

class Cat(object):
    def __init__(self, name):
        self.name = name

# In user code
baxter = Cat('Baxter')
print(baxter.name)  # => Baxter

With the @property decorator, we can now add some under-the-hood machinery without affecting the user code:

class Cat(object):
    def __init__(self, name):
        self._name = name
        self._name_access_count = 0

    @property
    def name(self):
        self._name_access_count += 1
        return self._name

# User code remains unchanged
baxter = Cat('Baxter')
print(baxter.name)  # => Baxter

# Also have information available about the number of times baxter's name was accessed
print(baxter._name_access_count)  # => 1
baxter.name                       # => 'Baxter'
print(baxter._name_access_count)  # => 2

This treatment of the @property decorator has been mentioned in some blog posts(1, 2) as one of the main use cases -- allowing us to initially write the simplest code possible, and then later on switch over to @propery-decorated methods when we need the functionality.

like image 166
2 revs Avatar answered Oct 04 '22 20:10

2 revs