class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
return self.number + add_num
def sub(self, sub_num):
return self.number - sub_num
Math(5).add(5)
I get 10 as expected
But if I do Math(5).add(5).sub(3):
I get this error AttributeError: 'int' object has no attribute 'sub'
for that to work your mehtods need to return self (or a fresh instance of Math):
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
self.number += add_num
return self
# or:
# return Math(self.number + add_num)
def sub(self, sub_num):
self.number -= sub_num
return self
# or:
# return Math(self.number - add_num)
def __str__(self):
return str(self.number)
m = Math(5).add(5).sub(3)
print(m)
# 7
the add here now behaves more like an __iadd__.
Of course.
What you do is essentially
a = Math(5) # a is a "Math" object
b = a.add(5) # b is what add() returns, i. e. an int
c = b.sub(3) # an int has no sub() method
I don't know what exactly you want to achieve: do you want add() and sub() to modify the object you are operating on? In this case, you can do
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
self.number = self.number + add_num
return self
def sub(self, sub_num):
self.number = self.number - sub_num
return self
If you don't want that, you can do instead
class Math:
def __init__(self, number):
self.number = number
def add(self, add_num):
return Math(self.number + add_num)
def sub(self, sub_num):
return Math(self.number - sub_num)
return self
In both cases, your intended way of chaining the calls works.
The value that you return is not an object of your Math class.
You must create an object of Math whose number attribute is your computed results and return that for your code to work.
When you execute return self.number + add_num, you return an integer, not an instance of your Math class. To solve this, you can change your add method to
return Math(self.number + add_num).
Related
Does anyone know how to update the value of an empty class instance in a method? My code does not work as expected.
class addition:
def __init__(self):
self.number = None
def add(self, data):
if self.number == None:
print('no')
self.number = data
else:
print('yes')
self.number + 10
return self.number
addition().add(4)
addition().add(5)
With addition().add(5) I want self.number to be equal to 4 but it stays equal to none. Please help!
You should either instantiate an object of your class or use a class method.
When you use a non static class, the members of your class receive the self identifier, meaning that they belong to that specific instance, that needs to be created. Like:
class addition:
def __init__(self):
self.number = None
def add(self, data):
if self.number == None:
print('no')
self.number = data
else:
print('yes')
self.number + 10
return self.number
addition1 = addition()
addition1.add(4)
addition1.add(5)
Otherwise, you can use a variable as a member of the class itself and declare the method a class method, then you don't need to instantiate an object and can call it directly. With a class method you don't use the word self, because you don't have an instance. Instead, you use (by convention) the word cls, referring to the class. Example:
class addition:
number = None
#classmethod
def add(cls, data):
if cls.number == None:
print('no')
cls.number = data
else:
print('yes')
cls.number + 10
return cls.number
addition.add(4)
addition.add(5)
Here's what I tried:
class Juice:
def __init__(self, name, capacity):
self.name = name
self.capacity = capacity
def __add__(self,other):
return (self.capacity+other.capacity)
Here I used only add method..
def __add__(self, other):
return (self.name+"&"+other.name)
def __str__(self):
return (self.name + ' ('+str(self.capacity)+'L)')
a = Juice('Orange', 1.5)
b = Juice('Apple', 2.0)
result = a + b
print(result)
I should have like: Orange&Apple(3.5L)
The __str__() method you wrote is executed when you run print() or str() on one instance.
When you added a and b the variable result has the value 3.5 so when you print it, it will print 3.5.
What you can do is change the __add__() method so that it returns the format you want.
In this solution I used fstrings to print the format of text you want from the __add__() method. like this:
class Juice:
def __init__(self, name, capacity):
self.name = name
self.capacity = capacity
def __add__(self, other):
return f"{self.name}&{other.name}({self.capacity + other.capacity}L)"
a = Juice("Orange", 1.5)
b = Juice("Apple", 2.0)
print(a + b)
this should output
Orange&Apple(3.5L)
I want the add method of my object Foo to return averaged summation. For the summation of just two objects it is straightforward:
class Foo():
def __init__(self, n):
self.n = n
def __add__(self, other):
return Foo((self.n + other.n)/2)
How to do this for N>2 objects? E.g. Foo(0) + Foo(1) + Foo(2) + Foo(3) should return Foo((0 + 1 + 2 + 3)/4), i.e. Foo(1.5).
========================================
Edit: Here's my solution
class Foo():
def __init__(self, n):
self.n = n
self._n = n
self._count = 1
def __add__(self, other):
out = Foo(self._n + other._n)
out._count = self._count + other._count
out.n = out.n/out._count
return out
Not the best way to get the arithmetic mean, but I needed to do it in this way. Also, this demonstrates how to do special additions of user defined objects, which return a function of the total sum of the objects. E.g. make __add__ return the square root of the sum of the objects:
class Bar():
def __init__(self, n):
self.n = n
self._n = n
def __add__(self, other):
out = Bar(self._n + other._n)
out.n = (out.n)**0.5
return out
One solution could be storing in the class TWO numbers: the average value and the number of samples:
class Foo:
def __init__(self, avg, count=1):
self.avg = avg
self.count = count
def __add__(self, other):
return Foo((self.avg*self.count + other.avg*other.count)
/
(self.count + other.count),
self.count + other.count)
Even better would be just storing the sum and compute the average only if/when requested.
I want to write a program that accepts as input a number p and produces as output a type-constructor for a number that obeys integer arithmetic modulo p.
So far I have
def IntegersModP(p):
N = type('IntegersMod%d' % p, (), {})
def __init__(self, x): self.val = x % p
def __add__(a, b): return N(a.val + b.val)
... (more functions) ...
attrs = {'__init__': __init__, '__add__': __add__, ... }
for name, f in attrs.items():
setattr(N, name, f)
return N
This works fine, but I'd like to know what the Pythonic way to do this is, which I understand would use metaclasses.
Like this:
def IntegerModP(p): # class factory function
class IntegerModP(object):
def __init__(self, x):
self.val = x % p
def __add__(a, b):
return IntegerModP(a.val + b.val)
def __str__(self):
return str(self.val)
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.val)
IntegerModP.__name__ = 'IntegerMod%s' % p # rename created class
return IntegerModP
IntegerMod4 = IntegerModP(4)
i = IntegerMod4(3)
j = IntegerMod4(2)
print i + j # 1
print repr(i + j) # IntegerMod4(1)
Metaclasses are for when your class needs to behave differently from a normal class or when you want to alter the behavior of the class statement. Neither of those apply here, so there's really no need to use a metaclass. In fact, you could just have one ModularInteger class with instances that record their value and modulus, but assuming you don't want to do that, it's still easy to do this with an ordinary class statement:
def integers_mod_p(p):
class IntegerModP(object):
def __init__(self, n):
self.n = n % IntegerModP.p
def typecheck(self, other):
try:
if self.p != other.p:
raise TypeError
except AttributeError:
raise TypeError
def __add__(self, other):
self.typecheck(other)
return IntegerModP(self.n + other.n)
def __sub__(self, other):
...
IntegerModP.p = p
IntegerModP.__name__ = 'IntegerMod{}'.format(p)
return IntegerModP
I'd like to implement an object, that bounds values within a given range after arithmetic operations have been applied to it. The code below works fine, but I'm pointlessly rewriting the methods. Surely there's a more elegant way of doing this. Is a metaclass the way to go?
def check_range(_operator):
def decorator1(instance,_val):
value = _operator(instance,_val)
if value > instance._upperbound:
value = instance._upperbound
if value < instance._lowerbound:
value = instance._lowerbound
instance.value = value
return Range(value, instance._lowerbound, instance._upperbound)
return decorator1
class Range(object):
'''
however you add, multiply or divide, it will always stay within boundaries
'''
def __init__(self, value, lowerbound, upperbound):
'''
#param lowerbound:
#param upperbound:
'''
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def init(self):
'''
set a random value within bounds
'''
self.value = random.uniform(self._lowerbound, self._upperbound)
def __str__(self):
return self.__repr__()
def __repr__(self):
return "<Range: %s>" % (self.value)
#check_range
def __mul__(self, other):
return self.value * other
#check_range
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self.div(other)
#check_range
def __add__(self, other):
return self.value + other
#check_range
def __sub__(self, other):
return self.value - other
It is possible to use a metaclass to apply a decorator to a set of function names, but I don't think that this is the way to go in your case. Applying the decorator in the class body on a function-by-function basis as you've done, with the #decorator syntax, I think is a very good option. (I think you've got a bug in your decorator, BTW: you probably do not want to set instance.value to anything; arithmetic operators usually don't mutate their operands).
Another approach I might use in your situation, kind of avoiding decorators all together, is to do something like this:
import operator
class Range(object):
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def _from_value(self, val):
val = max(min(val, self._upperbound), self._lowerbound)
# NOTE: it's nice to use type(self) instead of writing the class
# name explicitly; it then continues to work if you change the
# class name, or use a subclass
return type(self)(val, rng._lowerbound, rng._upperbound)
def _make_binary_method(fn):
# this is NOT a method, just a helper function that is used
# while the class body is being evaluated
def bin_op(self, other):
return self._from_value(fn(self.value, other))
return bin_op
__mul__ = _make_binary_method(operator.mul)
__div__ = _make_binary_method(operator.truediv)
__truediv__ = __div__
__add__ = _make_binary_method(operator.add)
__sub__ = _make_binary_method(operator.sub)
rng = Range(7, 0, 10)
print rng + 5
print rng * 50
print rng - 10
print rng / 100
printing
<Range: 10>
<Range: 10>
<Range: 0>
<Range: 0.07>
I suggest that you do NOT use a metaclass in this circumstance, but here is one way you could. Metaclasses are a useful tool, and if you're interested, it's nice to understand how to use them for when you really need them.
def check_range(fn):
def wrapper(self, other):
value = fn(self, other)
value = max(min(value, self._upperbound), self._lowerbound)
return type(self)(value, self._lowerbound, self._upperbound)
return wrapper
class ApplyDecoratorsType(type):
def __init__(cls, name, bases, attrs):
for decorator, names in attrs.get('_auto_decorate', ()):
for name in names:
fn = attrs.get(name, None)
if fn is not None:
setattr(cls, name, decorator(fn))
class Range(object):
__metaclass__ = ApplyDecoratorsType
_auto_decorate = (
(check_range,
'__mul__ __div__ __truediv__ __add__ __sub__'.split()),
)
def __init__(self, value, lowerbound, upperbound):
self._lowerbound = lowerbound
self._upperbound = upperbound
self.value = value
def __repr__(self):
return "<Range: %s>" % (self.value)
def __mul__(self, other):
return self.value * other
def __div__(self, other):
return self.value / float(other)
def __truediv__(self, other):
return self / other
def __add__(self, other):
return self.value + other
def __sub__(self, other):
return self.value - other
As it is wisely said about metaclasses: if you wonder wether you need them, then you don't.
I don't fully understand your problem, but I would create a BoundedValue class, and us only instances of said class into the class you are proposing.
class BoundedValue(object):
default_lower = 0
default_upper = 1
def __init__(self, upper=None, lower=None):
self.upper = upper or BoundedValue.default_upper
self.lower = lower or BoundedValue.default_lower
#property
def val(self):
return self._val
#val.setter
def val(self, value):
assert self.lower <= value <= self.upper
self._val = value
v = BoundedValue()
v.val = 0.5 # Correctly assigns the value 0.5
print v.val # prints 0.5
v.val = 10 # Throws assertion error
Of course you could (and should) change the assertion for the actual behavior you are looking for; also you can change the constructor to include the initialization value. I chose to make it an assignment post-construction via the property val.
Once you have this object, you can create your classes and use BoundedValue instances, instead of floats or ints.