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
Related
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.
I am importing several classes from a library with a common method, like
class BarClass1:
def __init__(self):
pass
def bar(self, x):
return x + 1
class BarClass2:
def __init__(self):
pass
def bar(self, x):
return x + 2
class BarClass3:
def __init__(self):
pass
def bar(self, x):
return x + 3
I want to add logging to the bar method of each class, and for that purpose I create children for these classes in the following way:
def log_something(x):
print(f'input is {x}')
class DerivedBarClass1(BarClass1):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass2(BarClass2):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
class DerivedBarClass3(BarClass3):
def __init__(self):
super().__init__()
def bar(self, x):
log_something(x)
return super().bar()
I feel I am doing a lot of code repetition, is there a simpler way of doing this? My main constraint is not being able to modify the code in BarClass1, BarClass2 or BarClass3.
If you can't modify the code, you can always monkey-patch the classes...
import functools
def add_logging_single_arg(f): # maybe a better name...
#functools.wraps(f)
def wrapper(self, x):
log_something(x)
return f(x)
return wrapper
for klass in [BarClass1, BarClass2, BarClass3]:
klass.bar = add_logging_single_arg(bar)
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)
class Neuron:
def __init__(self, inbound_neurons=[], label=''):
self.label = label
self.inbound_neurons = inbound_neurons
self.outbound_neurons = []
self.value = None
for n in self.inbound_neurons:
n.outbound_neurons.append(self)
def forward(self):
raise NotImplemented
class Input(Neuron):
def __init__(self):
Neuron.__init__(self)
def forward(self, value=None):
if value is not None:
self.value = value
class Add(Neuron):
def __init__(self, *inputs):
Neuron.__init__(self, inputs)
def forward(self):
for n in self.inputs:
self.value = self.value + n
Add() is the subclass of class Neuron, I have met some difficulties to use loop to add all the elements of the inputs array.
class Add(Neuron):
def __init__(self, *inputs):
Neuron.__init__(self, inputs)
def forward(self):
self.value = 0
for n in self.inbound_neurons:
self.value = self.value + n.value
return(self.value)
The function 'forward' in Class Add has a loop to sum all elements of inbound_neurons.
Firt off this line of code should be:
for n in self.inbound_neurons:
self.outbound_neurons.append(self)
self.inputs was never defined in your Class. In order to loop through the inputs, you'd have to have:
def __init__(self, *inputs):
self.inputs = inputs
Neuron.__init__(self, inputs)
However, it looks like inputs would be a list with two items, another list and maybe a string in it. These will not concate together. It looks like instead you want to sum the total of self.inbound_neurons.
It's not related to your question, but VERY important: you should NOT use mutable data types (as list) for function/method defaults.
Your code should be updated like this:
class Neuron:
def __init__(self, inbound_neurons=None, label=''):
self.inbound_neurons = inbound_neurons or []
# ...
Why you should do this way is explained here: "Least Astonishment" and the Mutable Default Argument
Suppose I have classes which have a common method (add), and I want to create a new class RandomPair which would contain a pair of objects of the same class and dispatch add to a random one.
E.g.,
class C1 (object):
def __init__ (self, title, plus = True):
self.title = title
self.plus = plus
self.acc = 0
def add (self, x):
if self.plus:
self.acc += x
else:
self.acc -= x
def __str__ (self):
return "C1(%s,%g)" % (self.title,self.acc)
class C2 (object):
def __init__ (self, title):
self.title = title
self.all = list()
def add (self, x, pos = None):
if pos:
self.all.insert(pos,x)
else:
self.all.append(x)
def __str__ (self):
return "C2(%s,%s)" % (self.title,self.all)
import random
class RandomPair (object):
def __init__ (self, klass, title, **kwargs):
self.objects = [klass(title + "#" + str(i), kwargs) for i in range(2)]
def add (self, *args, **kwargs):
self.objects[random.randint(0,1)].add(args,kwargs)
def __str__ (self):
return "\n".join([str(o) for o in self.objects])
Now, I want to be able to do
rp1 = RandomPair(C1,"test")
rp1.add(1)
rp1.add(2)
rp2 = RandomPair(C2,"test")
rp2.add(1)
rp2.add(2, pos=0)
but I get
TypeError: add() got multiple values for keyword argument 'self'
in self.objects[random.randint(0,1)].add(args,kwargs).
You need to apply the args and kwargs, using similar notation as when you defined the arguments. You need to do this in two places; in both RandomPair.__init__() and in RandomPair.add():
self.objects = [klass(title + "#" + str(i), **kwargs) for i in range(2)]
and
self.objects[random.randint(0,1)].add(*args, **kwargs)
otherwise you are just passing in two arguments, a tuple and a dictionary.
Your next problem is in C2.add(); you are using pos if it is empty; you want to inverse that test. Better still, test explicitly for None:
def add(self, x, pos=None):
if pos is None:
self.all.append(x)
else:
self.all.insert(pos,x)