property

getters & setters & stuff

2016-08-23

by
Kurt Neufeld
https://goo.gl/29yfjV

What is a property?

from the docs...


class property([fget[, fset[, fdel[, doc]]]])
  Return a property attribute for new-style classes
        

thanks a lot docs

What is a property?

it's basically a setter and/or a getter

it's basically a function where you don't need to put () on the end

it's basically a variable

it can be a function call or decorator

we need an example

Example


class Journal(object):

  def __init__(self):
    self._db = Database()

    @property
    def db(self):
        return self._db
        

journal = Journal()
print journal.db
        

Example Breakdown


class Journal(object):
        

Example Breakdown


class Journal(object): # object is important!
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database()
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database() # self._VAR is common pattern
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database() # self._VAR is common pattern

    @property
    def db(self):
        return self._db
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database() # self._VAR is common pattern

    @property
    def db(self):         # db is a getter
        return self._db   # return the variable
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database() # self._VAR is common pattern

    @property
    def db(self):         # db is a getter
        return self._db   # return the variable
        

journal = Journal()
print journal.db
        

Example Breakdown


class Journal(object): # object is important!

  def __init__(self):
    self._db = Database() # self._VAR is common pattern

    @property
    def db(self):         # db is a getter
        return self._db   # return the variable
        

journal = Journal()
print journal.db          # don't need '()' to call getter
        

Why use properties?

You may be asking yourself, why not just name the variable self.db instead of self._db?

That's because this was a simple example.

Even though in actual use it's more aesthetically pleasing.

Real Example


class Journal(object):

  def __init__(self):
    self._db = Database()

    @property
    def db(self):
        return self._db

    @property
    def model(self):
        return self.db.get_model('entry')

    @property
    def manager(self):
        return self.model.objects

    @property
    def entries(self):
        return self.manager.all() 
        

journal = Journal()
print journal.entries
# print journal._db.get_model('entries').objects.all()
        

Setters

That's great and all, but still only some syntactic sugar.

property is much more powerful when used as a setter.

Setter Example


class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x

  @property
  def x(self):
    return self._x

  @x.setter
  def x(self, val):
    assert val % 2 == 0
    self._x = val
        

Setter Example


class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x              # wait, wut?

  @property
  def x(self):
    return self._x

  @x.setter
  def x(self, val):
    assert val % 2 == 0
    self._x = val
        

Setter Example


class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x              # wait, wut?

  @property
  def x(self):              # I know this stuff now
    return self._x          # wait, where did _x come from?

  @x.setter
  def x(self, val):
    assert val % 2 == 0
    self._x = val
        

Setter Example


class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x              # wait, wut?

  @property
  def x(self):              # I know this stuff now
    return self._x          # wait, where did _x come from?

  @x.setter                 # @x.setter, setter for x property
  def x(self, val):
    assert val % 2 == 0
    self._x = val           # actual assignment
        

Setter Example


class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x              # wait, wut?

  @property
  def x(self):              # I know this stuff now
    return self._x          # wait, where did _x come from?

  @x.setter                 # @x.setter, setter for x property
  def x(self, val):
    assert val % 2 == 0     # OnlyEvens part
    self._x = val           # actual assignment
        

Setter Ideas

  • validate input
  • update caches
  • update some other object
  • reformat hard drive
  • use your imagination!

Remember this?


class property([fget[, fset[, fdel[, doc]]]])
  Return a property attribute for new-style classes
        

Equivalent...


class property([fget[, fset[, fdel[, doc]]]])
  Return a property attribute for new-style classes
        

class OnlyEvens(object):

  def __init__(self, x=0):
    self.x = x

  def get_x(self):
    return self._x

  def set_x(self, val):
    assert val % 2 == 0
    self._x = val

  x = property( get_x, set_x )
        

property

I use them as self documenting code. External objects are only allowed to use the property variables

More visually pleasing


print a.x.y.z
print a._x._y._z
print a.x().y().z()
      

deleter method

I've never used it. I can't think of a time when you'd use it. I couldn't find example code where anybody else used it.

I bet Dave has though.

ProTip ™

You can only call a property on an instance, not on a class.


class Foo(object):
  def __init__(self):
      self._x = 1

Foo.x = property( lambda self: self._x)

> print Foo.x
<property object at 0x104ce1d08>

> f = Foo()
> print f.x
1
      

reify

I came across this groovy pattern called reify. On the first call it's like a property but it changes itself into a variable.

This is handy if you have some one-time expensive setup to do, but don't necessarily need to pay the cost all the time.

Eg. create a database connection object some of the time

reify


# http://docs.pylonsproject.org/projects/pyramid/en/latest/_modules/pyramid/decorator.html#reify
class reify(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped

        from functools import update_wrapper
        update_wrapper(self, wrapped)

    def __get__(self, inst, objtype=None):
        if inst is None:
            return self
        val = self.wrapped(inst)
        setattr(inst, self.wrapped.__name__, val)
        return val
      

reify example


class Foo(object):
    @reify
    def jammy(self):
        print 'jammy called'
        return 1

> f = Foo()

> print f.jammy
jammy called
1

> print f.jammy
1
      

The End

questions?