26

I have a class like this:

class Foo(object):
    def __init__(self):
        self.bar = property(self.get_bar)

    def get_bar(self):
        return "bar"

print Foo().bar  #this prints <property object at 0x0051CB40>

I've seen How do Python properties work?, How to set a python property in __init__, but they all use the decorator method, which i don't because i want a different name. And I need access to self

How do i get the property to behave?

Community
  • 1
  • 1
gcq
  • 937
  • 3
  • 13
  • 19
  • 2
    Why would you like to use different name? Is there a reasonable reason? – Stan Prokop Feb 16 '14 at 14:01
  • Have you tried using property as a decorator or with a docstring? – Michael Aaron Safyan Feb 16 '14 at 14:02
  • A quick solution will be to call `__get__` on `bar` explicitly: `f = Foo();f.bar.__get__(f, type(f))` – Ashwini Chaudhary Feb 16 '14 at 14:16
  • 12
    (since this is the first web hit I found for my problem:) One other reason a property object can be returned instead of the property (even if the correct way of writing the property is used) is if the class is not instantiated into an object: print(Foo.bar) instead of print(Foo().bar) – Michael Scott Asato Cuthbert Sep 09 '15 at 20:36
  • 2
    @MichaelScottCuthbert I think you should add this as an alternative answer, despite not being directly related with the question. I had this problem and your comment saved me! Thank you! – jmpcm Mar 02 '18 at 15:21

6 Answers6

21

You need to make a minor change:

class Foo(object):

    def get_bar(self):
        return "bar"

    bar = property(get_bar)

print Foo().bar # prints bar

The property needs to be an attribute of the class, not the instance; that's how the descriptor protocol works.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
8

You can do it like this

class Foo(object):
    def __init__(self):
        self.__bar = None

    def get_bar(self):
        return self.__bar

    def set_bar(self, value):
        self.__bar = value

    bar = property(get_bar, set_bar)

foo = Foo()
print foo.bar    # None
foo.bar = 1
print foo.bar    # 1
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
8

The object is not instantiated.

class Foo(object):
  def get_bar(self):
    return "bar"

bar = Foo()
print(bar.get_bar)
David Parks
  • 30,789
  • 47
  • 185
  • 328
warchitect
  • 397
  • 1
  • 4
  • 15
  • This was the correct answer for me. I had done `Foo.get_bar` rather than `Foo().get_bar`. E.g., the object was not instantiated. – David Parks Jun 19 '19 at 17:10
  • Lol, it was my case too. After ~30 minutes of debbugging I found some place deep in the code where the property was read from class, not instance... – Andrey Semakin Nov 05 '19 at 12:24
7

You can also do it like shown here:

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

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

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

    @bar.deleter
    def bar(self):
        self._bar = None # for instance

which is equivalent to:

class Also_Foo(object):
    def __init__(self):
        self._bar = None

    def get_bar(self):
        return self._bar

    def set_bar(self, value):
        self._bar = value

    def del_bar(self):
        self._bar = None # for instance

    bar = property(fget=get_bar, fset=set_bar, fdel=del_bar, doc=None)

BUT without polluting the class namespace with get and set methods for each attribute.

You retain external direct access to the variable by using ._bar instead of .bar.

berna1111
  • 1,811
  • 1
  • 18
  • 23
0

My use case required defining bar as a property only under certain conditions, so I took jonrsharpe's advice and moved the definition into __new__ instead:

class Foo(object):
    def __new__(cls):
        cls.bar = property(cls.get_bar)
        return super(Foo, cls).__new__(cls)

    def get_bar(self):
        return "bar"

print(Foo().bar)  #this prints "bar"

However, nothing was gained by this "cleverness". Once the condition was met, the class contained the property. I might as well have defined it as jonrsharpe did.

jcomeau_ictx
  • 37,688
  • 6
  • 92
  • 107
0

Do the following if you want to do within init()

class Foo(object):
    def __init__(self):
        type(self).bar = property(self.get_bar)

    def get_bar(self):
       return "bar"
  • While this code snippet may be the solution, including a detailed explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – Shawn Hemelstrand Feb 26 '23 at 04:14