Comparing regexes with recursion - python

So I'm stuck here trying to recursively compare regexes with recursion. The user will create an object with two parameters, each a string of length one. These strings can only be "0", "1" or "2". But I want to recursively check if these strings point to another string as well. Like:
*
/ \
1 2
/ \
2 1
I can't figure out how to recursively point to a new object:
This is what I have so far:
class DotNode(object):
def __init__(self, _cargo, _left=None, _right=None):
self._cargo = _cargo
self._left = _left
self._right = _right
def __eq__(self, _other):
base = ['0','1','2']
if self._left in base and self._right in base:
return self._left == _other._left and self._right == _other._right
else:
while self._left not in base or self._right not in base:
new = self._left
new2 = self._right
new3 = _other._left
new4 = _other._right
return new._left == new3._left and new2._right == new4._right

You seem to already know how to do this: recursion. You want to call the __eq__function recursively here. I would also advice you check if the given cargo is one of the possible values in the constructor - or even better - every time the value is set.
class DotNode(object):
#property
def _cargo(self):
return self._vcargo
#_cargo.setter
def _cargo(self, val):
if val not in ['0', '1', '2']:
raise ValueError("{} is not a possible value for _cargo.".format(val))
self._vcargo = val
def __eq__(self, other):
return isinstance(other, DotNode) and self._cargo == other._cargo and self._left == other._left and self._right == other._right
Breaking it down
Of course you want to keep your constructor here. i just wrote the changed parts down. As you may have noticed you don't even need RegExes here, standard string comparison works just fine.
The _cargo property
I changed _cargo from a simple attribute to a property here. What does that mean? You get getters and setters à la Java that allow better control over the possible values. The actual data is stored in _vcargo of course someone could write to that attribute directly, but that would be downright stupid and you are certainly not responsible if someone uses your code in a way it was not intended. Should you try to set a value different from the possible values, a ValueError will be raised.
The __eq__ function
As you can see this function is actually very simple. Everything it does is compute whether the cargo of the node itself and the other node is equal. Now, if both subtrees are also equal the whole tree is equal. At the deepest level it will compare None with None if both trees are equal, because there will be no more subtrees.

Related

How to avoid infinite __eq__ recursion when class member contains other members of the same class

I need to match objects to objects and thus tried to encapsulate the data of the objects and the list of other objects that are a good match based on a certain criteria.
I wanted to write a custom dunder eq function to be able to compare my class based on value, so if the data and the objects that they are matched with are both the same, then the two instances would be equal.
My class:
class Data:
def __init__(self, value):
self.value = value
self.matched_to = []
def __repr__(self):
return "Value: {}, Matches: {}".format(self.value, self.matched_to)
def add_match(self, other: 'Data'):
self.matched_to.append(other)
def __eq__(self, other: 'Data'):
if type(self) != type(other):
return NotImplemented
if self.value != other.value:
return False
for self_match, other_match in zip(self.matched_to, other.matched_to):
if self_match != other_match:
return False
return True
elem1 = Data(10)
elem2 = Data(10)
elem3 = Data(10)
elem1.add_match(elem2)
elem2.add_match(elem1)
elem3.add_match(elem1)
print(elem1 == elem2) # False as the value are the same but not the match up
print(elem2 == elem3) # True as the value and matches are the same
And the terminal output is:
RecursionError: maximum recursion depth exceeded while calling a Python object
My first solution to this problem was to separate the match container and the data, but I encountered the same problem as my class member would still contain members of the same class.
My second idea was to compare the matched objects based on their id() as in the following code. However, I feel like that beats the whole purpose of the custom equality and then I should just simply check the id of the Data members from the beginning (thus not implementing a custom dunder eq)
def __eq__(self, other: 'Data'):
if type(self) != type(other):
return NotImplemented
if self.value != other.value:
return False
for self_match, other_match in zip(self.matched_to, other.matched_to):
if id(self_match) != id(other_match):
return False
return True
What would be the best way to solve this design flaw of mine? Thanks you!
The problem is not that objects contain objects of the same type, it's that you have cycles: elem1 contains elem2 and elem2 contains elem1, so it's not possible to resolve recursive operations because there is no terminal case to your recursion.
Don't do cycles if you intend to recurse.
For you understanding, your code is similar to:
l1 = []
l2 = []
l1.append(l2)
l2.append(l1)
l1 == l2
you receive same error maximum recursion depth exceeded

Python recursion and return

I'm new to the concept of recursion, had never practiced this magic in my coding experience. Something I'm really confused about Python recursion is the use of "return". To be more specific, I don't quite understand when to use return in some situations. I've seen cases where the return is used before recursion, and cases return is not needed at all.
For example:
A Leetcode Question: "Given the root node of a binary search tree (BST) and a value. You need to find the node in the BST that the node's value equals the given value. Return the subtree rooted with that node. If such node doesn't exist, you should return NULL."
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def searchBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
if root == None:
return root
if root.val == val:
return root
elif root.val > val:
return self.searchBST(root.left,val)
else:
return self.searchBST(root.right,val)
Why do I need to return "self.searchBST(root.left,val)" and "self.searchBST(root.right,val)"? If there is no return added for the two lines, would't the program still run recursively until the conditions of root.val == val or root== None is met, and a value is returned? (I know it's not the case in practice, I'm just trying to conceptualize it).
Moreover, could someone kindly show me the general guideline for using return in recursions? Thank you in advance!
If you just write:
self.searchBST(root.left,val)
instead of
return self.searchBST(root.left,val)
it will perform the recursive search but won't return the result back to the block in which it is invoked. When it gets to the value you want (or doesn't find it), that call will do
return root
But the previous call will just discard this value, rather than returning it back up the recursion chain.
A return statement exits the currently running function and returns a return value, which can then be used like any other value in Python: assigned to a variable, passed as the argument to another function, or ... returned as the return value of the calling function.
def some_func():
return 'result'
x = some_func() # after this, x == 'result'
If you call a function without capturing the return value, it just gets lost. So, if you just call some_func(), it will get executed.
some_func() # this works, but 'result' is lost
The same goes for a function that calls another function, even if that other function is itself:
def some_other_func1():
x = some_func()
return x
def some_other_func2():
return some_func() # exact same result as some_other_func1()
def some_recursive_func(n):
if n == 0:
print('Reached the end')
return n
else:
print(f'At {n}, going down')
return some_recursive_func(n-1)
print(some_recursive_func(3)) # prints a bunch of lines, but always prints `0` at the end
Let's take an even simpler example and you can apply the same logic to your method as well,
def fact(n):
#Base case
if n in [0, 1]:
return 1
#Recursion. Eg: when n is 2, this would eventually become 2 * 1 and would be returning 2 to the caller
return n * fact(n-1)
In general a recursive function will have 2 cases, one being the base case and the other being recursive call to self. And remember, this is a function and ideally is supposed to return something to the caller. That is where return statement would be needed. Otherwise you wouldn't be returning right value to the caller.
If there is no return added for the two lines, would't the program still run recursively until the conditions of root.val == val or root== None is met, and a value is returned
Value is returned yes. But it would be returned to previous call (and so on) and hence it would return to one of self.searchBST(root.left,val) or self.searchBST(root.right,val). You would still need to return from this point to the caller of the function. Hence you would need to have return self.searchBST(root.left,val) or return self.searchBST(root.right,val).

Pythonic way of sorting classes with possible Nones in variables

I have a class that looks more or less like this:
class Something():
def __init__(self,a=None,b=None):
self.a = a
self.b = b
I want to be able to sort it in a list, normally I'd just implement method like this:
def __lt__(self,other):
return (self.a, self.b) < (other.a, other.b)
But this will raise an error in following case:
sort([Something(1,None),Something(1,1)])
While I want is for None values to be treated as greated than or following output:
[Something(1,1),Something(1,None)]
First thing that somes to my mind is change __lt__ to:
def __lt__(self,other):
if self.a and other.a:
if self.a != other.a:
return self.a < other.a
elif self.a is None:
return True
elif other.a is None:
return False
if self.b and other.b:
if self.b != other.b:
return self.b < other.b
elif self.b is None:
return True
return False
This would give me the correct results but its just ugly and python usually has a simpler way, and I don't really want to do it for each variable that I use in sorting of my full class(omitted from here to make problem clearer).
So what is the pythonic way of solving this?
Note
I also tried following but I'm assuming that even better is possible:
This would:
def __lt__(self,other):
sorting_attributes = ['a', 'b']
for attribute in sorting_attributes:
self_value = getattr(self,attribute)
other_value = getattr(other,attribute)
if self_value and other_value:
if self_value != other_value:
return self_value < other_value
elif self_value is None:
return True
elif self_value is None:
return False
Really trying to internalize the Zen of Pyhton and I know that my code is ugly so how do I fix it?
A completely different design I thought of later (posted separately because it's so different it should really be evaluated independently):
Map all your attributes to tuples, where the first element of every tuple is a bool based on the None-ness of the attribute, and the second is the attribute value itself. None/non-None mismatches would short-circuit on the bool representing None-ness preventing the TypeError, everything else would fall back to comparing the good types:
def __lt__(self, other):
def _key(attr):
# Use attr is not None to make None less than everything, is None for greater
return (attr is None, attr)
return (_key(self.a), _key(self.b)) < (_key(other.a), _key(other.b))
Probably slightly slower than my other solution in the case where no None/non-None pair occurs, but much simpler code. It also has the advantage of continuing to raise TypeErrors when mismatched types other than None/non-None arise, rather than potentially misbehaving. I'd definitely call this one my Pythonic solution, even if it is slightly slower in the common case.
An easy way to do this is to convert None to infinity, i.e. float('inf'):
def __lt__(self, other):
def convert(i):
return float('inf') if i is None else i
return [convert(i) for i in (self.a, self.b)] < [convert(i) for i in (other.a, other.b)]
A solution for the general case (where there may not be a convenient "bigger than any value" solution, and you don't want the code to grow more complex as the number of attributes increases), which still operates as fast as possible in the presumed common case of no None values. It does assume TypeError means None was involved, so if you're likely to have mismatched types besides None, this gets more complicated, but frankly, a class design like that is painful to contemplate. This works for any scenario with two or more keys (so attrgetter returns a tuple) and only requires changing the names used to construct the attrgetter to add or remove fields to compare.
def __lt__(self, other, _key=operator.attrgetter('a', 'b')):
# Get the keys once for both inputs efficiently (avoids repeated lookup)
sattrs = _key(self)
oattrs = _key(other)
try:
return sattrs < oattrs # Fast path for no Nones or only paired Nones
except TypeError:
for sattr, oattr in zip(sattrs, oattrs):
# Only care if exactly one is None, because until then, must be equal, or TypeError
# wouldn't occur as we would have short-circuited
if (sattr is None) ^ (oattr is None):
# Exactly one is None, so if it's the right side, self is lesser
return oattr is None
# TypeError implied we should see a mismatch, so assert this to be sure
# we didn't have a non-None related type mismatch
assert False, "TypeError raised, but no None/non-None pair seen
A useful feature of this design is that under no circumstances are rich comparisons invoked for any given attribute more than once; the failed attempt at the fast path proves that there must (assuming invariant of types being either compatible or None golds) be a run of zero or more attribute pairs with equal values, followed by a None/non-None mismatch. Since everything we care about is known equal or a None/non-None mismatch, we don't need to invoke potentially expensive rich comparisons again, we just do cheap identity testing to find the None/non-None mismatch and then return based on which side was None.

Adding to Linked List

I've tried to write a function that takes three parameters: a linked list, a value, and a new value. The purpose of the function is to add the new value after the value in the linked list. Here's my function.
def addAfter(lis, value, newValue):
tracker = lis
while tracker != None:
if tracker['data'] == value:
newNode = {'data':newValue, 'next': tracker['next']}
tracker['next'] = newNode
break
else:
tracker = tracker['next']
For some reason I can't get this function to do anything. It doesn't change the list. I was wondering if someone could tell me what I'm doing wrong.
The problem is probably in how you're defining your initial nodes or list. For example, the following code works in Python3.4
def addAfter(lis, value, newValue):
tracker = lis
while tracker != None:
if tracker['data'] == value:
newNode = {'data':newValue, 'next': tracker['next']}
tracker['next'] = newNode
break
else:
tracker = tracker['next']
node1 = {'data':3,'next':None}
addAfter(node1,3,4)
print(node1)
print(node1['next'])
This outputs
{'next': {'next': None, 'data': 4}, 'data': 3}
{'next': None, 'data': 4}
As we'd expect it to. So there are several possibilities here
You're not using dictionaries at all - you defined a custom class and overloaded the setitem and getitem. I highly doubt this
You overloaded equals or hash so that the evaluations aren't correct. Doubt this too
You're using non-built-in objects as values and didn't overload the equals, so when you compare Foo() to Foo() it evaluates false. This seems somewhat likely. You might think that things are equal, when in fact Python is checking if they lie at the same memory location, not that their fields match
Your test code is wrong, you're printing out a copy of the data not the original data. This sort of thing is always a possibility
Try testing for each of those and let us know if none pan out. If not, provide a little more context, i.e., a scenario where the break is evident
Could you add a new class such as :
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
and then use the code of en_Knight, otherwise, the code will be hard to read

Most efficient way of comparing the contents of two class instances in python

I'm looking for the most efficient way of comparing the contents of two class instances. I have a list containing these class instances, and before appending to the list I want to determine if their property values are the same. This may seem trivial to most, but after perusing these forums I wasn't able specific to what I'm trying to do. Also note that I don't have an programming background.
This is what I have so far:
class BaseObject(object):
def __init__(self, name=''):
self._name = name
def __repr__(self):
return '<{0}: \'{1}\'>'.format(self.__class__.__name__, self.name)
def _compare(self, other, *attributes):
count = 0
if isinstance(other, self.__class__):
if len(attributes):
for attrib in attributes:
if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
if self.__dict__[attrib] == other.__dict__[attrib]:
count += 1
return (count == len(attributes))
else:
for attrib in self.__dict__.keys():
if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
if self.__dict__[attrib] == other.__dict__[attrib]:
count += 1
return (count == len(self.__dict__.keys()))
def _copy(self):
return (copy.deepcopy(self))
Before adding to my list, I'd do something like:
found = False
for instance in myList:
if instance._compare(newInstance):
found = True
Break
if not found: myList.append(newInstance)
However I'm unclear whether this is the most efficient or python-ic way of comparing the contents of instances of the same class.
Implement a __eq__ special method instead:
def __eq__(self, other, *attributes):
if not isinstance(other, type(self)):
return NotImplemented
if attributes:
d = float('NaN') # default that won't compare equal, even with itself
return all(self.__dict__.get(a, d) == other.__dict__.get(a, d) for a in attributes)
return self.__dict__ == other.__dict__
Now you can just use:
if newInstance in myList:
and Python will automatically use the __eq__ special method to test for equality.
In my version I retained the ability to pass in a limited set of attributes:
instance1.__eq__(instance2, 'attribute1', 'attribute2')
but using all() to make sure we only test as much as is needed.
Note that we return NotImplemented, a special singleton object to signal that the comparison is not supported; Python will ask the other object if it perhaps supports equality testing instead for that case.
You can implement the comparison magic method __eq__(self, other) for your class, then simply do
if instance == newInstance:
As you apparently don't know what attributes your instance will have, you could do:
def __eq__(self, other):
return isinstance(other, type(self)) and self.__dict__ == other.__dict__
Your method has one major flaw: if you have reference cycles with classes that both derive from BaseObject, your comparison will never finish and die with a stack overflow.
In addition, two objects of different classes but with the same attribute values compare as equal. Trivial example: any instance of BaseObject with no attributes will compare as equal to any instance of a BaseObject subclass with no attributes (because if issubclass(C, B) and a is an instance of C, then isinstance(a, B) returns True).
Finally, rather than writing a custom _compare method, just call it __eq__ and reap all the benefits of now being able to use the == operator (including contain testing in lists, container comparisons, etc.).
As a matter of personal preference, though, I'd stay away from that sort-of automatically-generated comparison, and explicitly compare explicit attributes.

Categories

Resources