I a newbie in Python OOP and I have a problem with the below program. When I run it, it gives me an error AttributeError: 'MyClass' object has no attribute 'sum'. This problem can be fix easily by replace the line sum = self.sum at each function compute_sqrtSum(), compute_SumSquare() and compute_SumCube() by sum = self.compute_Sum(). But if so, every time the program run these three functions, it has to run compute_Sum() once, in total three times. So is there a way that I can access to sum but only run compute_Sum() once?
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
def compute_Sum(self):
sum = self.x + self.y
self.sum = sum
return sum
def compute_sqrtSum(self):
sum = self.sum
sqrt_sum = sqrt(sum)
return sqrt_sum
def compute_SumSquare(self):
sum = self.sum
sum_sq = sum * sum
return sum_sq
def compute_SumCube(self):
sum = self.sum
sum_cb = sum * sum * sum
return sum_cb
user = MyClass(1, 2)
print(user.compute_sqrtSum())
print(user.compute_SumSquare())
print(user.compute_sqrtCube())
To have the attributes computed on-the-fly you could use properties to have a method called automatically to determine the value whenever it's needed. However this can become very slow if the value is accessed frequently either by users of the class or by the class itself if other methods within it also reference it.
A way to avoid that is to make the attributes "lazy" which means they aren't calculated until they're first referenced, but the value is cached so if it's needed again, the cached value is returned instead of the being re-calculated.
In the code below each method will only ever be run once because the lazy_property decorator✶—which isn't a property at all—has the side-effect of also creating an instance attribute of the same name as the class' property, which prevents it from being called again because of the way instance attributes are looked up in Python.
The similar but not the same as #furas' answer. It eliminates a lot of the repetitive code and also make it easy to apply the caching to other attributes as well, so they too, will never be calculated more than once.
✶ Lazily-evaluated Property Pattern in Python. Jun 30, 2013. stevenloria.com. Licensed under CC-BY 4.0 License
def lazy_property(fn):
"""Decorator that makes a property lazy-evaluated."""
attr_name = '_lazy_' + fn.__name__
#property
def _lazy_property(self):
if not hasattr(self, attr_name):
setattr(self, attr_name, fn(self)) # Create instance attribute.
return getattr(self, attr_name)
return _lazy_property
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
#lazy_property
def sum(self):
return self.x + self.y
#lazy_property
def sqrtSum(self):
return sqrt(self.sum)
#lazy_property
def SumSquare(self):
return self.sum * self.sum
#lazy_property
def SumCube(self):
return self.sum * self.sum * self.sum
Update
In Python 3.8 a cached_property decorator was added to the functools module which does basically the same thing as lazy_property above, so the code could simply be like this:
from functools import cached_property # Requires Python 3.8+
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
#cached_property
def sum(self):
return self.x + self.y
#cached_property
def sqrtSum(self):
return sqrt(self.sum)
#cached_property
def SumSquare(self):
return self.sum * self.sum
#cached_property
def SumCube(self):
return self.sum * self.sum * self.sum
inst = MyClass(4, 2)
print(inst.sum)
print(inst.SumCube)
Properties are your friends here.
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
#property
def sum(self):
return self.x + self.y
#property
def sqrt_sum(self):
return sqrt(self.sum)
#property
def sum_square(self):
return self.sum * self.sum
#property
def sum_cube(self):
return self.sum * self.sum * self.sum
Thus you could do
user = MyClass(1,2)
print(user.sum) # No parenthesis
print(user.sqrt_sum) # No parenthesis
print(user.sum_square) # No parenthesis
print(user.sum_cube) # No parenthesis
By the way, you should use builtin names (here sum) with caution
You could calculate self.sum directly in __init__ and then you don't have to calculate it again
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
self.sum = self.x + self.y
def compute_sum(self):
return self.sum
def compute_sqrt_sum(self):
return sqrt(self.sum)
def compute_sum_square(self):
#return self.sum * self.sum
return self.sum ** 2
def compute_sum_cube(self):
return self.sum ** 3
user = MyClass(1, 2)
print(user.compute_sqrt_sum())
print(user.compute_sum_square())
print(user.compute_sqrt_cube())
But if you change ie. user.x = 10 then it will use wrong sum.
So it can be good only if you don't want to change x,y,sum.
Eventually in __init__ you can set self.sum = None and calculate sum only when self.sum is None - so it would have to calculate it only once but it would have to always check if self.sum is None:
More or less like this
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
self.sum = None
def compute_sum(self):
if self.sum is None:
self.sum = self.x + self.y
return self.sum
def compute_sqrt_sum(self):
if self.sum is None:
self.compute_sum()
return sqrt(self.sum)
def compute_sum_square(self):
if self.sum is None:
self.compute_sum()
#return self.sum * self.sum
return self.sum ** 2
def compute_sum_cube(self):
if self.sum is None:
self.compute_sum()
return self.sum ** 3
user = MyClass(1, 2)
print(user.compute_sqrt_sum())
print(user.compute_sum_square())
print(user.compute_sqrt_cube())
But if you change ie. user.x = 10 then it will also use wrong sum.
So it can be good only if you don't want to change x,y,sum.
Related
I'm writing a Python class A with a method square() that returns a new instance of that class with its first attribute squared. For example:
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
I would like to use this method in a subclass B so that it returns an instance of B with x squared but all additional attributes of B unchanged (i. e. taken from the instance). I can get it to work by overwriting square() like this:
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def square(self):
return self.__class__(self.x**2, self.y)
If I don't overwrite the square() method, this little code example will fail because I need to pass a value for y in the constructor of B:
#test.py
class A:
def __init__(self, x):
self.x = x
def square(self):
return self.__class__(self.x**2)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
#def square(self):
# return self.__class__(self.x**2, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
$ python test.py
9
Traceback (most recent call last):
File "test.py", line 20, in <module>
b2 = b.square()
File "test.py", line 6, in square
return self.__class__(self.x**2)
TypeError: __init__() takes exactly 3 arguments (2 given)
Overwriting the method once isn't a problem. But A potentially has multiple methods similar to square() and there might be more sub(sub)classes. If possible, I would like to avoid overwriting all those methods in all those subclasses.
So my question is this:
Can I somehow implement the method square() in A so that it returns a new instance of the current subclass with x squared and all other attributes it needs for the constructor taken from self (kept constant)? Or do I have to go ahead and overwrite square() for each subclass?
Thanks in advance!
I'd suggest implementing .__copy__() (and possibly .__deepcopy__ as well) methods for both classes.
Then your squared can be simple method:
def squared(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
It will work with inheritance, assuming all child classes have correctly implemented __copy__ method.
EDIT: fixed typo with call to copy()
Full working example:
#test.py
from copy import copy
class A:
def __init__(self, x):
self.x = x
def square(self):
newObj = copy(self)
newObj.x = self.x **2
return newObj
def __copy__(self):
return A(self.x)
class B(A):
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
def __copy__(self):
return B(self.x, self.y)
a = A(3)
a2 = a.square()
print(a2.x)
b = B(4, 5)
b2 = b.square()
print(b2.x, b2.y)
check if the object contains y then return the right class instance:
class A:
x: int
def __init__(self, x):
self.x = x
def square(self):
if hasattr(self, 'y'):
return self.__class__(self.x ** 2, self.y)
return self.__class__(self.x**2)
class B(A):
y: int
def __init__(self, x, y):
super(B, self).__init__(x)
self.y = y
# def square(self):
# return self.__class__(self.x**2, self.y)
I'm following this code example from a python course:
class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
And I tried to implement this pattern to my own code:
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)
Note that I want the M_inv property to be None before I have run the setter the first time. Also, the setter solely relies on other properties of the class object, and not on input arguments.
The setter decorator generates an error:
NameError: name 'M_inv' is not defined
Why is this?
Your setter method should be like below:
#M_inv.setter
def 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)
The decorator #M_inv.setter and the function def M_inv(self): name should be same
The example is wrong.
EDIT: Example was using a setter in __init__ on purpose.
Getters and setters, even though they act like properties, are just methods that access a private attribute. That attribute must exist.
In the example, self.__x is never created.
Here is my suggested use :
class PCAModel(object):
def __init__(self):
# We create a private variable
self.__M_inv = None
#property
def M_inv(self):
# Accessing M_inv returns the value of the previously created variable
return self.__M_inv
#M_inv.setter
def M_inv(self): # Keep the same name than your propery
M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
self.__M_inv = np.linalg.inv(M)
I am currently playing around with classes and functions since i am not familiar with python and i would like to know how i can get addy(self, addx) to call addx.
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, addx):
z = addx() + 10
return z
one = test(1)
print(one.addy())
line 15, in print(one.addy()) TypeError: addy() missing 1
required positional argument: 'addx' Process finished with exit code 1
You need to call self from within a class method.
self.addx()
Also the addx parameter on this line shouldn't be there:
def addy(self, addx):
I think this is what you are going for:
class test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self):
z = self.addx() + 10
return z
one = test(1)
print(one.addy())
You've overcomplicated things by wrapping it in a class. Take it out and it'll work (mostly) the way you expect.
def add10(x):
return x+10
def add20(x):
return add10(add10(x))
Since you've wrapped it in the class you've complicated the namespace. It's no longer called addx or addy, so using those names throws a NameError. You have to use the qualified name instead.
class FooBar():
def __init__(self):
self.x = 10
def addx(self):
return self.x + 10 # Note the `self.` before the attribute...
def addy(self):
return self.addx() + 10 # ...and also before the method name.
Methods are always passed their owning object as a first argument when called, which is why we've got def addx(self): but then call with self.addx()
If you are attempting to relate addx in the signature of addy to the method addx, you can pass the string name of the method and use getattr:
class Test:
def __init__(self, x):
self.x = x
def addx(self):
y = self.x + 10
return y
def addy(self, func):
z = getattr(self, func)() + 10
return z
s = Test(3)
print(s.addy('addx'))
I want a self variable within init to update every time it is called e.g. every time I execute Data(10).plot, self.plot should reinitialise by parsing the self.n to the Plot class.
class Data(object):
def __init__(self, n):
self.n = n
self.plot = Plot(self.n)
def minus(self, x):
self.n -= x
return self.n
class Plot(object):
def __init__(self, n):
self.n = n
def double(self):
return self.n * 2
Another example: When I execute the following code, I want the answer variable to equal 16. Instead it equals 20. How do I implement this behaviour within the above classes?
data = Data(10)
data.minus(2)
answer = vcf.plot.double())
What you want is a property. This is a special type of attribute that calls a custom getter function when getting the value, so you can make it dynamically return the correct plot.
class Data(object):
def __init__(self, n):
self.n = n
#property
def plot(self):
return Plot(self.n)
def __sub__(self, x):
return Data(self.n - x)
As a side note, look at the data model to override python operators.
data = Data(10)
data -= 2
answer = data.plot.double() # Calls the `plot()` function to get a value for `data.plot`.
print(answer) # 16
Another way would be to link the Plot to the data, so when the data changes, the plot does to. A way to do it would be just to have it as an attribute, so when it changes, the attribute does too.
class Plot(object):
def __init__(self, data):
self.data = data
#property
def n(self):
return self.data.n
#n.setter
def n(self, x):
self.data.n = x
def double(self):
return self.n * 2
data = Data(10)
plot = Plot(data)
data.minus(2)
answer = plot.double() # 16
You don't need n in the Data-object:
class Data(object):
def __init__(self, n):
self.plot = Plot(n)
def minus(self, x):
self.plot.n -= x
class Plot(object):
def __init__(self, n):
self.n = n
def double(self):
return self.n * 2
Let's take a simple class as an example:
class Vector:
def __init__(self):
self.x = 1
self.y = 1
self.z = 1
What I would like is to give this class a variable called sum such that when I do
v = Vector()
v.sum
I am given the sum x+y+z (in this case 3). Of course I can easily just make a class method that does this, but then I would have to write v.sum() instead of v.sum. Is there any way to hide the fact that the class actually calls a function when asking for a variable?
Thanks in advance.
class Vector(object): # subclass object for new style class
def __init__(self):
self.x = 1
self.y = 1
self.z = 1
#property
def sum(self):
return self.x + self.y + self.z
>>> v = Vector()
>>> v.sum
3
http://docs.python.org/2/library/functions.html#property