简体   繁体   中英

Defining Python class instance attributes

Given a class definition that allows 3 possible inputs:

class FooBar(object):
    def __init__(self, x=None, y=None, z=None):
        if x is not None:
            self.x = x
        elif if y is not None:
            self.y = y
        elif if z is not None:
            self.z = z
        else:
            raise ValueError('Please supply either x,y or z')

This 3 inputs are related each other, lets say:

x = .5*y = .25*z

This also implies:

y = .5*z = 2*x

and

z = 2*y = 4*x

When creating a instance of FooBar() , the user need to supply one of those and the __init__ takes care of it.

Now I would like to do the following

  • If any one of the 3 variables are changed the others change following the relationship.

To try to accomplish that I did:

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

@x.setter
def x(self, value):
    self._x = value
    self._y = 2*self._x
    self._z = 4*self._x

And to the others:

@property
def y(self):
    return self._y

@y.setter
def y(self, value):
    self._y = value
    self._x = .5*self._y
    self._z = 2*self._y

@property
def z(self):
    return self._z

@z.setter
def z(self, value):
    self._z = value
    self._x = .25*self._z
    self._y = .5*self._z

Is this the correct approach?

I think you make this more complicated than you have to. If the variables are related, and one can fully be determined by the other, there is no need to store three variables. You can store one variable, and dynamically calculate the others. Like:

class Foo(object):

    def __init__(self, x=None, y=None, z=None):
        if x is not None:
            self.x = x
        elif x is not None:
            self.y = y
        elif z is not None:
            self.z = z
        else:
            raise ValueError('Provide an x, y, or z value.')

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

    @x.setter
    def x(self, value):
        self._x = x

    @property
    def y(self):
        return self._x / 2.0

    @y.setter
    def y(self, value):
        self._x = 2 * value

    @property
    def z(self):
        return self._x / 4.0

    @z.setter
    def z(self, value):
        self._x = 4 * value

We thus store only a _x attribute on the class, and all the rest of the getters and setters, use the _x attribute (we can of course use _y or _z instead).

Furthermore something that is not very elegant is that a programmer can instantiate a Foo(x=1, y=425) . As you can see, that means that it contains inconsistency. Perhaps it is worth raising an error in that case.

You can ensure that you only have one parameter provided by adding the following check in the init module:

class Foo(object):

    def __init__(self, x=None, y=None, z=None):
        
        if x is not None:
            self.x = x
        elif x is not None:
            self.y = y
        elif z is not None:
            self.z = z
        else:
            raise ValueError('Provide an x, y, or z value.')

    # ...

Here we thus construct a list of all non- None values, if there is more than one, the programmer provided two or more values, and then it is better to raise an exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM