Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Field variables in a class (Python)

Tags:

python

object

Why is this piece of code allowed, and is it good practice to use it? I've had plenty of Java experience, making initializing variables in Python look very informal.

class MyClass(object):
    def __init__(object):
        pass            #no mention of x here
    def setX(self,x):
        self.x = x      #why is this allowed
    def getX(self):
        return self.x
like image 403
user217285 Avatar asked Oct 28 '25 00:10

user217285


2 Answers

The question "why is this allowed?" is best answered by "why would it not be allowed?" In general, any Python object can have an attribute of any name assigned to it at any time. Unlike in languages like Java, where an object's full memory layout needs to be known at compile time, in Python object attributes are stored in dictionaries. Dictionaries can hold any number of items, so there is nothing to keep you from assigning to any attribute at any time. __init__() is special in that it is executed upon instantiation, but it in no way defines the set of attribute names permitted on an object.

There is a way to prevent willy-nilly attribute assignment, and that is with the __slots__ attribute. However, this is primarily for memory optimization; the inability to add arbitrary attributes is a side effect. And it still doesn't keep you from initializing attributes outside __init__():

class MyClass(object):
    __slots__ = ("x",)    # x is the only attribute allowed
    def setX(self,x):
        self.x = x
    def getX(self):
        return self.x

You can also write a custom __setattr__ method to filter out attributes you don't want to allow, but this is easily bypassed and in general is contrary to the Python philosophy of "we're all adults here."

It is poor programming practice to have a method that relies on state that's not available upon instantiation, but you seem to understand that.

like image 107
kindall Avatar answered Oct 29 '25 16:10

kindall


In a lot of ways, python's a very simple language. It has no special magic that prevents you from being able to set attributes at specific times. If you want that sort of magic, you need to build it yourself.

In a lot of ways, you can begin to think of class instances (and classes themselves) as wrappers around dictionaries. In fact, both classes and instances have a __dict__ attribute:

>>> class Foo(object):
...   x = 'bar'
... 
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 'x': 'bar', '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None})
>>> Foo().__dict__
{}

Why it works.

Setting an attribute on a class instance actually calls the "magic" __setattr__ hook method (if present). If not present, python simply stores the value in the object's dictionary. Since the default is just a dictionary, you can set any value in the dict at any time.

Is it a good idea?

Of course, whether or not it's a good idea boils down to your design philosophy. It's usually best to put some sort of default for the value in the initializer so that the rest of your code doesn't have to guard against AttributeError that could be raised if you called getX before setX was called...


I will say that it is generally not idiomatic to write simple getters and setters in python. In your code, it's most idiomatic just to have an attribute x:

class MyClass(object):
    """This is my class.

    Attributes:
        x: the "x" attribute.
    """
    def __init__(self, x=None):
        self.x = x

Usage looks like:

m = MyClass('foo')
print(m.x)
m.x = 'bar'
print(m.x)

I think that the usual Java argument for setters and getters here is "what if you want to do other actions when you set a value -- Sure, you don't need that now, but maybe you will someday in the future ..."

Actually, Python's got you covered there too. You can always change your simple attribute into a property without breaking your interface:

class MyClass(object):
    def __init__(self, x=None):
        self._x = x
    @property
    def x(self):
        return self._x
    @x.setter
    def x(self, value):
        self._x = value

And now you have all the "advantages" of a java getter/setter pair, but to an outside user, your interface hasn't changed:

m = MyClass('foo')
print(m.x)
like image 35
mgilson Avatar answered Oct 29 '25 15:10

mgilson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!