What's the difference between a Python "property" and "attribute"?

Question:

I am generally confused about the difference between a “property” and an “attribute”, and can’t find a great resource to concisely detail the differences.

Asked By: Carson

||

Answers:

In general speaking terms a property and an attribute are the same thing. However, there is a property decorator in Python which provides getter/setter access to an attribute (or other data).

class MyObject(object):
    # This is a normal attribute
    foo = 1

    @property
    def bar(self):
        return self.foo

    @bar.setter
    def bar(self, value):
        self.foo = value


obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
Answered By: six8

With a property you have complete control on its getter, setter and deleter methods, which you don’t have (if not using caveats) with an attribute.

class A(object):
    _x = 0
    '''A._x is an attribute'''

    @property
    def x(self):
        '''
        A.x is a property
        This is the getter method
        '''
        return self._x

    @x.setter
    def x(self, value):
        """
        This is the setter method
        where I can check it's not assigned a value < 0
        """
        if value < 0:
            raise ValueError("Must be >= 0")
        self._x = value

>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
  File "ex.py", line 15, in <module>
    a.x = -1
  File "ex.py", line 9, in x
    raise ValueError("Must be >= 0")
ValueError: Must be >= 0
Answered By: neurino

The property allows you to get and set values like you would normal attributes, but underneath there is a method being called translating it into a getter and setter for you. It’s really just a convenience to cut down on the boilerplate of calling getters and setters.

Lets say for example, you had a class that held some x and y coordinates for something you needed. To set them you might want to do something like:

myObj.x = 5
myObj.y = 10

That is much easier to look at and think about than writing:

myObj.setX(5)
myObj.setY(10)

The problem is, what if one day your class changes such that you need to offset your x and y by some value? Now you would need to go in and change your class definition and all of the code that calls it, which could be really time consuming and error prone. The property allows you to use the former syntax while giving you the flexibility of change of the latter.

In Python, you can define getters, setters, and delete methods with the property function. If you just want the read property, there is also a @property decorator you can add above your method.

http://docs.python.org/library/functions.html#property

Answered By: falcojr

Properties are a special kind of attribute. Basically, when Python encounters the following code:

spam = SomeObject()
print(spam.eggs)

it looks up eggs in spam, and then examines eggs to see if it has a __get__, __set__, or __delete__ method — if it does, it’s a property. If it is a property, instead of just returning the eggs object (as it would for any other attribute) it will call the __get__ method (since we were doing lookup) and return whatever that method returns.

More information about Python’s data model and descriptors.

Answered By: Ethan Furman

I learnt 2 differences from site of Bernd Klein, in summary:

1. A property is a more convenient way to achieve data encapsulation

For example, let’s say you have a public attribute length. Later on, your project requires you to encapsulate it, i.e. to change it to private and provide a getter and setter => you have to change the the code you wrote before:

# Old code
obj1.length = obj1.length + obj2.length
# New code (using private attributes and getter and setter)
obj1.set_length(obj1.get_length() + obj2.get_length()) # => this is ugly

If you use @property and @length.setter => you don’t need to change that old code.

2. A property can encapsulate multiple attributes

class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name = name
    self.__physic_health = physic_health 
    self.__mental_health = mental_health 

  @property
  def condition(self):
    health = self.__physic_health + self.__mental_health
    if(health < 5.0):
      return "I feel bad!"
    elif health < 8.0:
      return "I am ok!"
    else:
      return "Great!"

In this example, __physic_health and __mental_health are private and cannot be accessed directly from outside.

Answered By: Tin Luu

There is also one not obvious difference that i use to cache or refresh data , often we have a function connected to class attribute. For instance i need to read file once and keep content assigned to the attribute so the value is cached:

class Misc():
        def __init__(self):
            self.test = self.test_func()

        def test_func(self):
            print 'func running'
            return 'func value'

cl = Misc()
print cl.test
print cl.test

Output:

func running
func value
func value

We accessed the attribute twice but our function was fired only once. Changing the above example to use property will cause attribute’s value refresh each time you access it:

class Misc():

    @property
    def test(self):
        print 'func running'
        return 'func value'

cl = Misc()
print cl.test
print cl.test

Output:

func running
func value
func running
func value
Answered By: brc

I like to think that, if you want to set a restriction for an attribute, use a property.

Although all attributes are public, generally programmers differentiate public and private attributes with an underscore(_). Consider the following class,

class A:
    def __init__(self):
        self.b = 3    # To show public
        self._c = 4   # To show private

Here, b attribute is intended to be accessed from outside class A. But, readers of this class might wonder, can b attribute be set from outside class A?

If we intend to not set b from outside, we can show this intention with @property.

class A:
    def __init__(self):
        self._c = 4   # To show private
   
    @property
    def b(self):
        return 3

Now, b can’t be set.

a = A()
print(a.b)   # prints 3
a.b = 7      # Raises AttributeError

Or, if you wish to set only certain values,

class A:
    @property 
    def b(self):
        return self._b
    
    @b.setter
    def b(self, val):
        if val < 0:
            raise ValueError("b can't be negative")
        self._b = val

a = A()
a.b = 6     # OK
a.b = -5    # Raises ValueError
Answered By: Sadman Sakib
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.