Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Misuse of 'property' reserved word

So I have some god forsaken legacy code that uses the reserved word property, um wrong. In a base class that gets inherited they have basically implemented.

class TestClass(object):

    def __init__(self, property):
        self._property = property

    @property
    def property(self):
        return self._property


test = TestClass('test property')
print(test.property)

Which runs without error. If you add another method below that you get,

class TestClass2(object):

    def __init__(self, property):
        self._property = property

    @property
    def property(self):
        return self._property

    @property
    def other_property(self):
        return 'test other property'


test = TestClass2('test property')
print(test.property)
print(test.other_property)

Which throws:

---> 10     @property
     11     def other_property(self):
     12         print('test other property')

TypeError: 'property' object is not callable

Because you know you have overwritten property in the local namespace.

class TestClass3(object):

    def __init__(self, property):
        self._property = property

    @property
    def other_property(self):
        return 'test other property'

    @property
    def property(self):
        return self._property


test = TestClass3('test property')
print(test.property)
print(test.other_property)

You can work around this if you always define your property overwrite at the bottom of your class. If the property method is only defined on the base class you inherit from things also work out, because namespaces.

class TestClass4(TestClass):

    def __init__(self, property):
        super(TestClass4, self).__init__(property)

    @property
    def other_property(self):
        return 'test other property'


test = TestClass4('test property')
print(test.property)
print(test.other_property)

My righteous indignation says that we MUST update this variable name in the huge amount of legacy code, because GAAAAH, but other than having to remember to add new methods above the definition of property definition in the rarely modified base class, this doesn't actually break anything right?

like image 567
TristanMatthews Avatar asked May 19 '16 05:05

TristanMatthews


2 Answers

dont shadow builtins... with almost no refactoring at all you can avoid shadowing the builtin entirely

use __getattr__ instead of @property to return your _property member ...

class TestClass(object):
    def __init__(self):
        self._property = 12

    def __getattr__(self,item):
        if item == "property": 
           #do your original getter code for `property` here ... 
           # now you have not overwritten the property keyword at all
           return getattr(self,"_property") # just return the variable
class TestClass2(TestClass):
    def __init__(self):
        self._property = 67

print TestClass2().property

class MySubClass(TestClass):
    @property
    def a_property(self):
        return 5

print MySubClass().property
print MySubClass().a_property

really, as an aside, theres not any good reason imho to use @property in python. all it does is end up confusing other programmers later and obscuring the fact that you are actually calling a function. I used to do it regularly ... I now avoid it unless I have a very very compelling reason not to

like image 81
Joran Beasley Avatar answered Nov 11 '22 08:11

Joran Beasley


Yes, if you always add new methods above the definition of the property method nothing will break. So put a nice big comment to that effect in the code. Hopefully, anyone wanting to override property in a derived class will look at the base class first and see your comment...

BTW, the property arg to the __init__ method also shadows property, but I guess that's not an issue.

Ideally, someone should clean this mess up, but I understand that it may not be cost-effective to do that.

Also, I'm somewhat baffled why the original coder made property an @property in the first place. That construct should only be used when the value of the attribute has to be calculated dynamically, not for simply returning a static attribute. Perhaps they were new to Python and they were told that's the way to do getters in Python...

like image 34
PM 2Ring Avatar answered Nov 11 '22 10:11

PM 2Ring