'Instance attribute {} defined outside __init__' warning when using property() built-in

已回答

I'm wanting a class constructor to raise an exception if a particular value is empty. From what I can understand the best way to do this is using the built-in property() function.

The docs show a leading underscore when setting the attribute in the class' constructor: `self._x = None`. From all the examples I've seen (and my own experimentation), however, the setter method will not be called during instantiation unless the leading underscore is removed from the attribute name: `self.x = arg`. The rest of the code remains the same.

Functionally this works as desired, but as each setter is setting 'self._x' and not 'self.x' I get an 'Instance attribute x defined outside __init__' for each one. This behaviour is the same whether I use the property() function directly, or use decorators.

I understand why I'm getting the message (attribute names are technically different), but was hoping PyCharm's code inspection might be able to put it in context. Maybe it can't.


Am I doing something wrong (I'm new to Python), or do I have any options beyond suppressing the messages?

0

Hi Chris! Could you please provide a code snippet with your example?

0
Avatar
Permanently deleted user

Sure - here's the example from the property() docs that doesn't use decorators (I removed the delx() method):

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    x = property(getx, setx, "I'm the 'x' property.")

 

In order to have the setter called when the class is instantiated, however, the code needs the following amendment:

class C:
    def __init__(self, value):
        self.x = value

 

Note how there's no leading underscore before the 'x' attribute. As a result I now get the warning for this line in the setx() method:

self._x = value

 

I'm getting the warning because `_x` and `x` aren't the same attribute, and `self._x` hasn't been set in the constructor. The problem is that, while this behaviour might be technically correct, from everything I've read it on getters and setters in Python in seems that when using the property() function it might not be practically correct. In order to get rid of the warning without actively suppressing it I need to amend the constructor again:

class C:
    def __init__(self, value):
self._x = None self.x = value

or
self._x = self.x = value


The only place I've come across a constructor like this being used with getters and setters was in a Stack Overflow question where someone was asking about this exact warning and had used the first example to hide it - they didn't get a satisfactory answer, just told that setting the `_x` attribute first was redundant and non-Pythonic, and 'surely there's a better way to suppress that message?' Nobody came up with one, which is why I'm here...

 

0

Ah, I see, thank you! Yeap, that's kind of tricky usecase, personally I believe one should include self._x = None in the constructor, but I'm not 100% sure about the style of this solution so I created a ticket in our bug tracker: PY-25263.

0
Avatar
Permanently deleted user

Aye, I can see both sides myself, although I'd personally go with it being redundant - it could make the constructor quite ugly if you have a lot of setters (although if you're using a lot of setters in Python you're arguably doing something wrong).

It also looks a lot worse with long variable names:  

self._a_descriptive_varname = self.a_descriptive_varname = value



Many thanks for submitting the ticket.

0

请先登录再写评论。