Carson
Carson

Reputation: 17741

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

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

Upvotes: 243

Views: 94435

Answers (9)

chi11ax
chi11ax

Reputation: 631

For one use case, I use @property to load data on demand and cache

class Foo:
    def __init__(self):
        self._bar = None

    @property
    def bar(self):
        if _bar is None:
            _bar = self.load_bar_dataset_from_database()
        return _bar

When my fields contain dataframes for analysis, they are loaded when first accessed and I don't initialize the whole object by loading dataframes from the database all at once, but can also be cached to use in further analysis.

Also prevents "setting" for consistency.

Upvotes: 0

Ethan Furman
Ethan Furman

Reputation: 69031

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

spam = SomeObject()
print(spam.eggs)

it looks up eggs in SomeObject1, and then examines eggs to see if it has a __get__, __set__, or __delete__ method -- if it does, it's a property, and Python will call the __get__ method (since we were doing lookup) and return whatever that method returns. If it is not a property, then eggs is looked up in spam, and whatever is found there will be returned.

More information about Python's data model and descriptors.


1 Many thanks to Robert Seimer for the correction on the lookup sequence.

Upvotes: 252

autf
autf

Reputation: 13

Attribute is actually at the object.

Property is propagated by proxy. (Its value may be calculated on the fly.)


See also

Upvotes: 0

Sadman Sakib
Sadman Sakib

Reputation: 400

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

Upvotes: 10

Tin Luu
Tin Luu

Reputation: 1687

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.

Upvotes: 19

neurino
neurino

Reputation: 12395

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

Upvotes: 108

brc
brc

Reputation: 269

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

Upvotes: 11

falcojr
falcojr

Reputation: 1444

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

Upvotes: 21

six8
six8

Reputation: 2990

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

Upvotes: 27

Related Questions