I want to create a check on inputs of a particular class, I have the following made up example:
class NmbPair:
def __init__(self, a = None, b = None):
self.a = a
self.b = b
def __eq__(self, other):
if self.a == other.a and self.b == other.b:
return True
return False
class NmbOperation:
def __init__(self, *, NmbPair1, NmbPair2):
if not self.check(NmbPair1, NmbPair2): ## this is the check
return
self.NmbPair1 = NmbPair1
self.NmbPair2 = NmbPair2
self._add_first_nmb()
def check(self, a, b):
if a == b:
return False
def _add_first_nmb(self):
self.sum_a = self.NmbPair1.a + self.NmbPair2.a
so i want to check that the input NmbPairs are not the same, and if they are I do not want an instance of NmbOperation to be created.
For example:
t1 = NmbPair(2, 3)
t2 = NmbPair(2, 2)
Op1 = NmbOperation(NmbPair1 = t1, NmbPair2 = t2)
print(Op1.sum_a)
But this throws the error:
AttributeError: 'NmbOperation' object has no attribute 'sum_a'
I'm not quite sure what I'm doing wrong
You are creating a NmbOperation object for which the __init__ method immediately returns before executing the lines
self.NmbPair1 = NmbPair1
self.NmbPair2 = NmbPair2
self._add_first_nmb()
This is because self.check(NmbPair1, NmbPair2) returns None, so not self.check(NmbPair1, NmbPair2) is True.
Therefore, the attribute sum_a is never set since _add_first_nmb is never called.
Your check method is equivalent to:
def check(self, a, b):
if a == b:
return False
else:
return None
You probably want
def check(self, a, b):
return not a == b
Related
I have a Common (or could call Base) class.
from enum import Enum
class Common:
def a(self, condition):
if condition == True:
return self.Keys.F.value
else:
return self.Keys.G.value
def b(self):
return Common.a(self, True) * 10
and multiple other classes, all of them use the Common class.
class KlassOne:
class Keys(Enum):
E = 0
F = 1
G = 2
def func(self, attribute):
w = Common.a(self, condition=bool(attribute))
x = Common.b(self)
return w, x
class KlassTwo:
class Keys(Enum):
E = 0
G = 1
F = 2
def func(self, attribute):
y = Common.a(self, condition=bool(attribute))
z = Common.b(self)
return y, z
how do I avoid the Common.a(self, ...) way of implementing this.
Is there an alternative to it?
You can instantiate your class :
class KlassOne:
def func(self, attribute):
common = Common()
w = common.a(condition=bool(attribute))
x = common.b()
return w, x
Your recent update to the question means that Common looks more like a mixin. (I would refactor b like this)
class Common:
def a(self, condition):
if condition == True:
return self.Keys.F.value
else:
return self.Keys.G.value
def b(self):
return self.a(True) * 10
The above Common does not need to import from enum.
Now your other classes can inherit from the above, just as a mixin:
from enum import Enum
class KlassOne(Common):
class Keys(Enum):
E = 0
F = 1
G = 2
def func(self, attribute):
w = self.a(condition=bool(attribute))
x = self.b()
return w, x
First, the Common.a(... is not necessary inside Common:
class Common:
def a(self, condition):
if condition == True:
return 1
else:
return 2
def b(self):
return self.a(True) * 10
Second, if you want to pass self of KlassOne to a method of class Common, then KlassOne actually should be an instance of class Common:
class KlassOne:
def func(self, attribute):
w = Common.a(self, condition=bool(attribute)) # <- here self is instance of KlassOne
x = Common.b(self)
return w, x
This is an argument strongly in favour of inheritance:
class KlassOne(Common):
def func(self, attribute):
w = self.a(condition=bool(attribute))
x = self.b()
return w, x
If you want to use composition, you would need to instantiate Common, e.g. like
class KlassOne:
def __init__(self):
self.common = Common()
def func(self, attribute):
w = self.common.a(condition=bool(attribute))
x = self.common.b()
return w, x
A slightly long question to sufficiently explain the background...
Assuming there's a builtin class A:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
return self.a == other.a
It's expected to compare in this way:
a1, a2 = A(1), A(2)
a1 == a2 # False
For some reason, the team introduced a wrapper on top of it (The code example doesn't actually wrap A to simplify the code complexity.)
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
return self.pa == other.pa
Again, it's expected to compare in this way:
wa1, wa2 = WrapperA(1), WrapperA(2)
wa1 == wa2 # False
Although it's expected to use either A or WrapperA, the problem is some code bases contain both usages, thus following comparison failed:
a, wa = A(), WrapperA()
wa == a # AttributeError
a == wa # AttributeError
A known solution is to modify __eq__:
For wa == a:
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
return self.pa == other.pa
For a == wa:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
if isinstance(other, WrapperA):
return self.a == other.pa
return self.a == other.a
Modifying WrapperA is expected. For A, since it is a builtin thing, two solutions are:
Use setattr to extend A to support WrapperA.
setattr(A, '__eq__', eq_that_supports_WrapperA)
Enforce developer to only compare wa == a (And then don't care about a == wa).
1st option is obviously ugly with duplicated implementation, and 2nd gives developer unnecessary "surprise". So my question is, is there an elegant way to replace any usage of a == wa to wa == a by the Python implementation internally?
Quoting the comment from MisterMiyagi under the question:
Note that == is generally expected to work across all types. A.__eq__ requiring other to be an A is actually a bug that should be fixed. It should at the very least return NotImplemented when it cannot make a decision
This is important, not just a question of style. In fact, according to the documentation:
When a binary (or in-place) method returns NotImplemented the interpreter will try the reflected operation on the other type.
Thus if you just apply MisterMiyagi's comment and fix the logic of __eq__, you'll see your code works fine already:
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
if isinstance(other, A):
return self.a == other.a
return NotImplemented
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
elif isinstance(other, WrapperA):
return self.pa == other.pa
return NotImplemented
# Trying it
a = A(5)
wrap_a = WrapperA(5)
print(a == wrap_a)
print(wrap_a == a)
wrap_a.pa = 7
print(a == wrap_a)
print(wrap_a == a)
print(f'{wrap_a.pa=}')
Yields:
True
True
False
False
wrap_a.pa=7
Under the hood, a == wrap_a calls A.__eq__ first, which returns NotImplemented. Python then automatically tries WrapperA.__eq__ instead.
I dont really like this whole thing, since I think that wrapping a builtin and using different attribute names will lead to unexpected stuff, but anyway, this will work for you
import inspect
class A:
def __init__(self, a=None):
self.a = a
def __eq__(self, other):
return self.a == other.a
class WrapperA:
def __init__(self, a=None):
self.pa = a
def __eq__(self, other):
if isinstance(other, A):
return self.pa == other.a
return self.pa == other.pa
def __getattribute__(self, item):
# Figure out who tried to get the attribute
# If the item requested was 'a', check if A's __eq__ method called us,
# in that case return pa instead
caller = inspect.stack()[1]
if item == 'a' and getattr(caller, 'function') == '__eq__' and isinstance(caller.frame.f_locals.get('self'), A):
return super(WrapperA, self).__getattribute__('pa')
return super(WrapperA, self).__getattribute__(item)
a = A(5)
wrap_a = WrapperA(5)
print(a == wrap_a)
print(wrap_a == a)
wrap_a.pa = 7
print(a == wrap_a)
print(wrap_a == a)
print(f'{wrap_a.pa=}')
Output:
True
True
False
False
wrap_a.pa=7
Similar to Ron Serruyas answer:
This uses __getattr__ instead of __getattribute__, where the first one is only called if the second one raises an AttributeError or explicitly calls it (ref). This means if the wrapper does not implement __eq__ and the equality should only be performed on the underlying data structure (stored in objects of class A), a working example is given by:
class A(object):
def __init__(self, internal_data=None):
self._internal_data = internal_data
def __eq__(self, other):
return self._internal_data == other._internal_data
class WrapperA(object):
def __init__(self, a_object: A):
self._a = a_object
def __getattr__(self, attribute):
if attribute != '_a': # This is neccessary to prevent recursive calls
return getattr(self._a, attribute)
a1 = A(internal_data=1)
a2 = A(internal_data=2)
wa1 = WrapperA(a1)
wa2 = WrapperA(a2)
print(
a1 == a1,
a1 == a2,
wa1 == wa1,
a1 == wa1,
a2 == wa2,
wa1 == a1)
>>> True False True True True True
I have to following class that will make an object with chainable methods that derive from class variables. Since this code is quite repetitive, my challenge is to make a decorator that can apply over method a, b and c. The problem I am facing is that I cannot seem to find a way to construct a wrapper that will return the instance (self). Is there a better way to construct this?
class Test:
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
self.call_chain = []
def a(self, truth):
def func():
return self._a == truth
self.call_chain.append(func)
return self
def b(self, truth):
def func():
return self._b == truth
self.call_chain.append(func)
return self
def c(self, val):
def func():
return self._c == val
self.call_chain.append(func)
return self
def evaluate(self):
try:
for f in self.call_chain:
if f() == False:
raise ValueError('False encountered')
except ValueError:
self.call_chain.clear()
return False
self.call_chain.clear()
return True
It works chained like this:
c = Test(True, False, 13)
c.a(True).b(False).c(13).evaluate()
The trick is to store the arguments to the function as part of the call chain. The easiest way is to use functools.partial objects.
from functools import wraps, partial
def chain(func):
#wraps(func)
def wrapper(self, *args, **kwargs):
suspended = partial(func, self, *args, **kwargs)
self.call_chain.append(suspended)
return self
return wrapper
class Test:
def __init__(self, a, b, c):
self.call_chain = []
self._a = a
self._b = b
self._c = c
#chain
def a(self, val):
return self._a == val
#chain
def b(self, val):
return self._b == val
#chain
def c(self, val):
return self._c == val
def evaluate(self):
try:
for f in self.call_chain:
if f() == False:
raise ValueError('False encountered')
except ValueError:
self.call_chain.clear()
return False
self.call_chain.clear()
return True
c = Test(True, False, 13)
c.a(True).b(False).c(13).evaluate() # True
c.a(True).b(False).c(11).evaluate() # False
the 'list.remove' function does not compare objects by value
suppose the code is:
class item:
def __init__(self, a, b):
self.feild1 = a
self.field2 = b
a = item(1,4)
b = item(1,4)
l = [a]
l.remove(b) # doesn't remove l[0]
Because you don't provide a __eq__ implementation, your class inherits the method from object. object.__eq__ doesn't compare the values of attribute, it just checks to see that id(a) == id(b). You need to write your own __eq__:
class item:
def __init__(self, a, b):
self.field1 = a
self.field2 = b
def __eq__(self, other):
if not isinstance(other, item):
return NotImplemented
return self.field1 == other.field1 and self.field2 == other.field2
a = item(1,4)
b = item(1,4)
l = [a]
l.remove(b)
print(l)
# []
I want to call a nested function directly, like this:
Template('/path/to/file').expect('key').to_be_in('second_key')
Template('/path/to/file').expect('key').to_be('value')
I tried this:
class Template(object):
def __init__(self, path):
self.content = json.load(open(path, 'r'))
def expect(self, a):
def to_be_in(b):
b = self.content[b]
return a in b
def to_be(b):
a = self.content[b]
return a == b
But I get the following error:
Template('~/template.json').expect('template_name').to_be_in('domains')
AttributeError: 'NoneType' object has no attribute 'to_be_in'
How to achieve that in Python ?
You have to return an object which provides a to_be_in member that is a function, to wit (example only):
class Template_Expect(object):
def __init__(self, template, a):
self.template = template
self.a = a
def to_be_in(self, b):
b = self.template.content[b]
return self.a in b
def to_be(self, b):
a = self.template.content[b]
return a == b
class Template(object):
def __init__(self, path):
self.content = json.load(open(path, 'r'))
def expect(self, a):
return Template_Expect(self, a)