Let's say I have the following classes:
import math
class LineSegment:
def __init__(
self,
origin,
termination,
):
self.origin = origin
self.termination = termination
self.length = self.calculate_length()
def calculate_length(self):
return math.sqrt(
(self.origin.x - self.termination.x) ** 2
+ (self.origin.y - self.termination.y) ** 2
)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
An object of the LineSegment class is composed of two objects of the Point class. Now, let's say I initialize an object as so:
this_origin = Point(x=0, y=0)
this_termination = Point(x=1, y=1)
this_line_segment = LineSegment(origin=this_origin, termination=this_termination)
Note: The initialization of the line segment automatically calculates its length. This is critical to other parts of the codebase, and cannot be changed. I can see its length like this:
print(this_line_segment.length) # This prints "1.4142135623730951" to the console.
Now, I need to mutate one parameter of this_line_segment's sub-objects:
this_line_segment.origin.x = 1
However, the this_line_segments length attribute does not update based on the new origin's x coordinate:
print(this_line_segment.length) # This still prints "1.4142135623730951" to the console.
What is the pythonic way to implement updating a class's attributes when one of the attributes they are dependent upon changes?
Option 1: Getter and Setter Methods
In other object-oriented programming languages, the behavior you desire, adding additional logic when accessing the value of an instance variable, is typically implemented by "getter" and "setter" methods on all instance variables in the object:
class LineSegment:
def __init__(
self,
origin,
termination,
):
self._origin = origin
self._termination = termination
# getter method for origin
def get_origin(self):
return self._origin
# setter method for origin
def set_origin(self,new_origin):
self._origin = new_origin
# getter method for termination
def get_termination(self):
return self._termination
# setter method for termination
def set_termination(self,new_termination):
self._termination = new_termination
def get_length(self):
return math.sqrt(
(self.get_origin().x - self.get_termination().x) ** 2
+ (self.get_origin().y - self.get_termination().y) ** 2
) #Calls the getters here, rather than the instance vars in case
# getter logic is added in the future
So that the extra length calculation is performed every time you get() the length variable, and instead of this_line_segment.origin.x = 1, you do:
new_origin = this_line_segment.get_origin()
new_origin.x = 1
this_line_segment.set_origin(new_origin)
print(this_line_segment.get_length())
(Note that I use _ in front of variables to denote that they are private and should only be accessed via getters and setters. For example, the variable length should never be set by the user--only through the LineSegment class.)
However, explicit getters and setters are clearly a clunky way to manage variables in Python, where the lenient access protections make accessing them directly more transparent.
Option 2: The #property decorator
A more Pythonic way to add getting and setting logic is the #property decorator, as #progmatico points out in their comment, which calls decorated getter and setter methods when an instance variable is accessed. Since all we need to do is calculate the length whenever it is needed, we can leave the other instance variables public for now:
class LineSegment:
def __init__(
self,
origin,
termination,
):
self.origin = origin
self.termination = termination
# getter method for length
#property
def length(self):
return math.sqrt(
(self.origin.x - self.termination.x) ** 2
+ (self.origin.y - self.termination.y) ** 2
)
And usage:
this_line_segment = LineSegment(origin=Point(x=0,y=0),
termination=Point(x=1,y=1))
print(this_line_segment.length) # Prints 1.4142135623730951
this_line_segment.origin.x = 1
print(this_line_segment.length) # Prints 1.0
Tested in Python 3.7.7.
Note: We must do the length calculation in the length getter and not upon initialization of the LineSegment. We can't do the length calculation in the setter methods for the origin and termination instance variables and thus also in the initialization because the Point object is mutable, and mutating it does not call LineSegment's setter method. Although we could do this in Option 1, it would lead to an antipattern, in which we would have to recalculate every other instance variable in the setter for each instance variable of an object in the cases for which the instance variables depend on one another.
Related
What is a proper way to set an class atribute which is valid for all objects and the class itself?
My Code looks like this:
class Bacteria(Contour):
min_bacteria_size = 22.56 * 10 ** -15
def __init__(self, contour):
super().__init__(contour.contour, contour.hierarchy)
self.area = contour.area
self.mean_gray = contour.mean_gray
rect = cv2.minAreaRect(self.contour)
self.center = rect[0]
self.width = rect[1][0]
self.height = rect[1][1]
self.rotation = rect[2]
#property
def min_bacteria_size(self):
return Bacteria.min_bacteria_size
#min_bacteria_size.setter
def min_bacteria_size(self, min_bacteria_size):
# min_bacteria_size in fm²
self.min_bacteria_size = min_bacteria_size * 10 ** -15
For min_bacteria_size there is default value, but it should be possible to change this value for all objects and the class itself. Since i want to set the variable min_bacteria_size in femto (10^-15) units i tried to use property setter but it doesn´t worked:
Bacteria.min_bacteria_size = 50
print(Bacteria.min_bacteria_size)
>> 50
Your code is almost just right - just two points of confusion: the name of
the class attribute where the value is stored must not be the same name as the property itself.
The major problem with your code is that when you create the property, you overwrite the default class value. Then your setter sets the new value to the instance only (self.min_bacteria-size) - instead of the class (self.__class__.min_bacteria_size) - however, if it were written exactly like that, it would overwrite the property itself - so it could be used only once.
Then, there is a 3rdy point, if you will sometimes to see the value in raw meters (the number already multiplied by 10e-15) and sometimes the number in femtometers (the human friendly "50") - you should make BOTH numbers available when reading from the class instances (even if the raw pre-multiplied metric value is only used internally by the class).
So, one way to go is to have an ordinary class attribute which holds the raw value in meters, and a property that will scale and store that same attribute, to be consumed by the code that uses the class:
class Bacteria(Contour):
min_bacteria_size_raw = 22.56 * 10e-15
def __init__(self, contour):
...
#property
def min_bacteria_size(self):
return self.__class__.min_bacteria_size_raw / 10e-15
#min_bacteria_size.setter
def min_bacteria_size(self, min_bacteria_size):
# min_bacteria_size in fm²
self.__class__.min_bacteria_size_raw = min_bacteria_size * 10e-15
And here is the class working (with a dummy "contour" class) on the interactive prompt:
In [9]: b = Bacteria(1)
In [10]: b.min_bacteria_size
Out[10]: 22.56
In [11]: b.min_bacteria_size = 50
In [12]: b.min_bacteria_size
Out[12]: 50.0
In [13]: b.min_bacteria_size_raw
Out[13]: 5e-13
# And checking a new instance:
In [14]: Bacteria(2).min_bacteria_size
Out[14]: 50.0
Note that te way properties work, you can't retrieve the transformed value from the class itself with Bacteria.min_bacteria_size: that will retrieve the property object itself. It is possible to create an object just like a property, but that will return the guarded value instead of itself when called on the class - but unless you really need this, or if you will want this for several classes and values, it would be overcomplicate the code. You can easily just invert the logic, and keep the class attribute value in fentometres, and use the property to get the multiplied meter value - that way the human friendly value is readl available as a simple class attribute, just like the multiplied value is in this implementation:
In [15]: Bacteria.min_bacteria_size
Out[15]: <property at 0x7fa61e469f40>
In [16]: Bacteria.min_bacteria_size_raw
Out[16]: 5e-13
I think you're looking for classmethod in python.
class A:
a = 3
#classmethod
def set_a(cls, value):
cls.a = value * 10 # or whatever calculation
def update_a(self): # use in other functions as normal
self.set_a(10)
a = A()
a.set_a(3) # use classmethod
print(A.a) # 30
a.update_a() # or normal
print(A.a) # 100
class SetSize:
def __init__(self, storage_name):
self.storage_name = storage_name
def __set__(self, instance, value):
instance.__dict__[self.storage_name] = value * 10 ** -15
class Bacteria(Contour):
min_bacteria_size = SetSize(‘min_bacteria_size’)
I couldn’t test it but this idea would help you I think.
I was wondering if all self. has to be defined in __init__, for example, i have this code right here:
class Colour:
def __init__(self, r, g, b):
self._red = r
self._green = g
self._blue = b
self._rgb = (self._red, self._green, self._blue)
def luminosity(self):
self._luminosity = 0.5 * ((max(self._red, self._green, self._blue))/255)+((min(self._red, self._green, self._blue))/255)
return self._luminosity
Am i right to define self.luminosity in the function def luminosity(self) or should i define it in __init__?
In this case, you don't need to define it, because it's only set and then returned when you could directly return the calculated value from your method!
Additionally, you can simplify the calculation a little, though I am not sure it is really luminosity, as there are a variety of interpretations different to yours
def luminosity(self):
return 0.5 * (
max(self._red, self._green, self._blue) + \
min(self._red, self._green, self._blue)
) / 255
If instead, you were caching the value (which may make sense if you do a more complex calculation or call the luminosity method many times), it would make sense to set it in __init__() and check before calculating (effectively caching the last call)
As #laol suggests, you can also use #property to simplify some of the its use
And finally, you can take advantage of your combined RGB for the calculation
class Colour():
def __init__(self, r, g, b):
self._red = r
self._green = g
self._blue = b
self._luminosity = None
#property
def rgb(self):
return (self._red, self._green, self._blue)
#property
def luminosity(self):
if self._luminosity is None:
self._luminosity = 0.5 * (max(self.rgb) + min(self.rgb)) / 255
return self._luminosity
c = Colour(128,100,100)
print(c.luminosity)
0.44705882352941173
Extending this even further, setting new values for the color components can set the cached value back to None, triggering re-calculation on the next call (rather than immediately, saving some calculation if many changes are made before the value is wanted), but this is left as an exercise to the reader
I suggest to define it as a property:
#property
def luminosity(self):
return 0.5 * ((max(self._red, self._green, self._blue))/255)+((min(self._red, self._green, self._blue))/255)
By this you can directly return it from any Colour c by
c.luminosity
No, instance variables do not need to be defined in __init__. Instance variables are completely dynamic and can be added any time either in a method or outside of the object (see note). However, if you don't define them, you have created an object access protocol that needs to be managed. Suppose another method is added:
def half_luminosity(self):
return self._luminosity/2
It is an error to call it before luminosity. This code will raise AttributeError if its called at the wrong time. You could assign self._luminosity = None in __init__ and check it
def half_luminosity(self):
if self._luminosity is None:
raise ValueError("Attempt to use luminosity before set")
but that's not much different than
def half_luminosity(self):
if not hasattr(self, '_luminosity'):
raise ValueError("Attempt to use luminosity before set")
If you have a class that is setup in more than one step, either way will do. PEP8 favors the first because its easier for a futurer reader to see what's going on.
NOTE: Classes that use __slots__ or one of the getattr methods can change the rules as can C extensions.
I'm using the property and setter decorators int he following way:
class PCAModel(object):
def __init__(self):
self.M_inv = None
#property
def M_inv(self):
return self.__M_inv
#M_inv.setter
def set_M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
This generates an error in the __init__ function because my setter is not taking an argument:
TypeError: M_inv() takes 1 positional argument but 2 were given
I don't want to set the M_inv with an argument, since the calculations of M_inv rely solely on other properties of the class object. I could put a dummy argument in the setter:
#M_inv.setter
def set_M_inv(self, foo):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
but that feels dirty. Is there a better way to get around this?
You are missing the point of setters and getters, although the names are pretty self-explanatory. If your parameter is calculated independently from what you are trying to set (you want to ommit the argument in the setter), then a setter is just not needed at all. Since all you wanna do is calculate this parameter for each instance, just calculate and return the value in the getter, so you will be getting the correct, newly-calculated value each time you try to access your parameter.
#property
def M_inv(self):
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
return np.linalg.inv(M)
In my code I have a class, where one method is responsible for filtering some data. To allow customization for descendants I would like to define filtering function as a class attribute as per below:
def my_filter_func(x):
return x % 2 == 0
class FilterClass(object):
filter_func = my_filter_func
def filter_data(self, data):
return filter(self.filter_func, data)
class FilterClassDescendant(FilterClass):
filter_func = my_filter_func2
However, such code leads to TypeError, as filter_func receives "self" as first argument.
What is a pythonic way to handle such use cases? Perhaps, I should define my "filter_func" as a regular class method?
You could just add it as a plain old attribute?
def my_filter_func(x):
return x % 2 == 0
class FilterClass(object):
def __init__(self):
self.filter_func = my_filter_func
def filter_data(self, data):
return filter(self.filter_func, data)
Alternatively, force it to be a staticmethod:
def my_filter_func(x):
return x % 2 == 0
class FilterClass(object):
filter_func = staticmethod(my_filter_func)
def filter_data(self, data):
return filter(self.filter_func, data)
Python has a lot of magic within. One of those magics has something to do with transforming functions into UnboundMethod objects (when assigned to the class, and not to an class' instance).
When you assign a function (And I'm not sure whether it applies to any callable or just functions), Python converts it to an UnboundMethod object (i.e. an object which can be called using an instance or not).
Under normal conditions, you can call your UnboundMethod as normal:
def myfunction(a, b):
return a + b
class A(object):
a = myfunction
A.a(1, 2)
#prints 3
This will not fail. However, there's a distinct case when you try to call it from an instance:
A().a(1, 2)
This will fail since when an instance gets (say, internal getattr) an attribute which is an UnboundMethod, it returns a copy of such method with the im_self member populated (im_self and im_func are members of UnboundMethod). The function you intended to call, is in the im_func member. When you call this method, you're actually calling im_func with, additionally, the value in im_self. So, the function needs an additional parameter (the first one, which will stand for self).
To avoid this magic, Python has two possible decorators:
If you want to pass the function as-is, you must use #staticmethod. In this case, you will have the function not converted to UnboundMethod. However, you will not be able to access the calling class, except as a global reference.
If you want to have the same, but be able to access the current class (disregarding whether the function it is called from an instance or from a class), then your function should have another first argument (INSTEAD of self: cls) which is a reference to the class, and the decorator to use is #classmethod.
Examples:
class A(object):
a = staticmethod(lambda a, b: a + b)
A.a(1, 2)
A().a(1, 2)
Both will work.
Another example:
def add_print(cls, a, b):
print cls.__name__
return a + b
class A(object):
ap = classmethod(add_print)
class B(A):
pass
A.ap(1, 2)
B.ap(1, 2)
A().ap(1, 2)
B().ap(1, 2)
Check this by yourseld and enjoy the magic.
I have the following example setup:
class Feet:
def __init__ (self, value = 0.0):
self.value = value
self.units = "f"
def feet(self):
return self.value
class Meters:
def __init__(self, value = 0.0):
self.value = value
self.units = "m"
def feet(self):
# This is probably not an accurate conversion.
return self.value * 2.54 * 10
class Distance (Feet, Meters):
def __init__(self, type = Feet()):
Feet.__init__(self)
Meters.__init__(self)
print type.feet() -- Prints 254.0
self = type
print self.feet() -- Prints 254.0
dist = Distance(Meters(10.0))
print dist.units -- Prints = "m"
print dist.value -- Prints = 0.0
print dist.feet() -- Prints = 0.0
I can't seem to understand why when I initialize the class to a Meters Class type, and assign it 10.0, I don't keep the 10.0. However the Units seem to have stayed correct. Am I missing something about how this is being setup?
My understanding is that I'm creating an "instance" of Meters, and assigning it to the "self" variable of Distance. If the self value couldn't be modified I could understand if my units was "f", but my units is "m" so it's obviously assigning the Meters class to self, but it's not taking the instantiated values, which I find quite odd.
To be honest I don't even know what I would google in this case, so I apologize I haven't done a whole lot of googling, most of what I found didn't apply at all to this type of problem.
Additionally, my plan was to basically "cast" it to the same type no matter what you passed in, for example for feet I would return the self instance for the Feet class, and in the Meters class I would return Feet(self.Value * 2.54 * 10) so I would always have my distance in Feet.
so for Feet feet becomes
def feet(self):
return self
for Meters feet becomes
def feet(self):
return Feet(self.value * 2.54 * 10)
To Recap, is there a reason that I'm able to pass in 1 of 2 classes as part of initialization, but it doesn't take my initialization parameters for that class?
It's really unclear to me why I can assign "self" in the distance class, and before it returns it appears to have the right initialization but upon returning it doesn't work right.
The thing is that you are inheriting from 2 classes Feet and Meters. Both classes have the same methods. In your Distance.__init__() method, you are overriding Feet's methods with Meters' methods when doing this:
Feet.__init__(self)
Meters.__init__(self)
What I would have done differently:
class Distance(object):
def __init__(self, meters=None, feet=None):
self.feet = feet
self.meters = meters
Then you can do something like:
distance = Distance(meters=Meters(12))
print distance.meters.value
print distance.meters.type
# Here do whatever you want with them
You can pass in the two objects at the same time. And do some other stuff with
the two objects if the are both different than None.
There's absolutely no reason to inherit from either Feet or Meters here, let alone both. This is a classic case of composition, rather than inheritance, especially since you are passing the units class as a parameter. Remove that subclassing, and in __init__ you can do self.type = type.
Other answers cover the problems you have with inheriting, but haven't covered your rebinding of self.
Inside a method (such as __init__), self is simply a local name bound to the instance. You are perfectly at liberty to rebind the name, but that simply makes self refer to something else. It doesn't affect the instance.
In this case, when __init__ returns the self name goes out of scope, but the original instance is assigned to dist just as though you hadn't rebound the name self.
Note that __init__ is an initializer, not a constructor. Python does also allow you to define a custom constructor for a class (__new__), and the constructor can change the object that is returned. However you don't need to use it here.
This line:
self = type
doesn't do what you think it does. You think this is an assignment statement, in which the object refered to by self takes on the attributes of type, a la C++.
Python doesn't have assignments in the same sense that other languages have.
What that line does is to bind the local name self to the object to which type is currently bound. It has absolutely no effect outside of Distance.__init__(), and virtually no effect on the object to which self was previously bound.