Parsing: functional inheritance for class methods in Python - python

For some context I am trying to write a parser for a search language. At the moment I have a bunch of class methods for accepting a token given that it satisfies a certain criterion, however it seems that their general structure/format can be encapsulated with the (newly written) method _accept_generic.
class Parser(object):
...
def now(self):
# now = self._tokens[self._pos] if not self.end() else None
# print(now, self._pos)
return self._tokens[self._pos] if not self.end() else None
def end(self):
return self._pos == self._length
def step(self):
self._pos += 1
def _accept_generic(self, criterion):
if not self.end():
if criterion:
self.step()
return True
return False
def _accept(self, char):
return self._accept_generic(self.now() == char)
# if not self.end():
# if self.now() == char:
# self.step()
# return True
# return False
def _accept_re(self, regex):
return self._accept_generic(regex.match(self.now()))
# if not self.end():
# if regex.match(self.now()):
# self.step()
# return True
# return False
def _accept_any_res(self, *regexes):
return self._accept_generic(any([r.match(self.now()) for r in regexes]))
# if any([r.match(self.now()) for r in regexes]):
# self.step()
# return True
# return False
def _accept_all_res(self, *regexes):
return self._accept_generic(all([r.match(self.now()) for r in regexes]))
# if all([r.match(self.now()) for r in regexes]):
# self.step()
# return True
# return False
The problem with the current code is that it will throw errors (since the criterion is being evaluated in the function argument rather than within the if statement if not self.end()). Is there any way using e.g. functools to allow the functions to inherit the generic's structure, with the ability to specify new arguments in the child functions' code, and not have to write the same code blocks repeatedly? functools.partialmethods doesn't really do what I'm looking for.

You can make the criterion a function and pass it to _accept_generic:
def _accept(self, char):
return self._accept_generic(lambda c=char: self.now() == c)
Then call in _accept_generic:
def _accept_generic(self, criterion):
if not self.end():
if criterion():
self.step()
return True
return False

Related

Function Chaining in Python, ignore rest of chain if a segment returns False

The title pretty much explains the problem. I don't know if there's a practical solution to this or if I'm being too picky over the behavior of my code.
This article was hinting in the right direction, but I never got any code to work.
https://medium.com/#adamshort/python-gems-5-silent-function-chaining-a6501b3ef07e
Here's an example of the functionality that I want:
class Calc:
def __init__(self, n=0):
self.n = n
def add(self, n):
self.n += n
return self
def might_return_false(self):
return False
def print(self):
print(self.n)
return self
w = Calc()
# The rest of the chain after might_return_false should be ignored
addedTwice = w.add(5).might_return_false().add(5).print()
w.print() # Should print 5
print(addedTwice) # Should print False
I think the article meant something more or less like below (but I prefer the other answer using exception, as it's more readable and better testable).
Create a helper class:
class Empty:
def __call__(self, *args, **kwargs):
return self
def __getattr__(self, *args, **kwargs):
return self
def print(self, *args, **kwargs):
return False
and
def might_return_false(self):
return Empty()
Exceptions are a great way to interrupt a chained operation:
class CalcError(Exception):
pass
class Calc:
def __init__(self, n: int = 0):
self.n = n
def add(self, n: int) -> 'Calc':
self.n += n
return self
def might_raise(self) -> 'Calc':
raise CalcError
def __str__(self) -> str:
return str(self.n)
w = Calc()
try:
w.add(5).might_raise().add(5)
addedTwice = True
except CalcError:
addedTwice = False
print(w) # prints 5
print(addedTwice) # prints False
You could also do chains like:
w = Calc()
num_added = 0
try:
w.add(5)
num_added += 1
w.add(5)
num_added += 1
w.might_raise()
w.add(5)
num_added += 1
w.add(5)
num_added += 1
except CalcError:
print(f"Stopped after {num_added} additions")
If you attempt to do this with return instead of raise, you need to check the status at each step of the chain so that you can switch off to some other branch of code (probably via an if block). Raising an exception has the very useful property of immediately interrupting execution, no matter where you are, and taking you straight to the nearest matching except.

how can I write a decorator that changes with arguments

I have to write a decorator def that takes a validator def as argument. If the validator returned true it should decorate main to execute some code and if it returned false it should print an error.
I have tried to write two def in decorator with an if statement to return two different defs but it is not working.
the functionality and the logic MUST be exactly like i said because of online judging (validation must be done outside of decorator)
Here's an example:
#define decorator...
def validator(x):
return x>=0
#decorator(validator)
def f(x):
return x**0.5
print(f(4)) #should print 2
print(f(-4)) #should print error
Here is what you can do
#define decorator...
def validator(x):
return x>=0
def deco(validator):
def decorator(func):
def wrapper_decorator(*args, **kwargs):
if validator(*args, **kwargs):
return func(*args, **kwargs)
else:
print("error")
return
return wrapper_decorator
return decorator
#deco(validator)
def f(x):
return x**0.5
print(f(4)) #should print 2
print(f(-4)) #should print error
The answers everyone has answered are basically correct. However for your case, you require an additional function that acts as a validator. Hence you can add in another outer def to take in the function of the validator and check if it returns True/False.
Decorators can be written as example
def hello_decorator(func):
def inner1(*args, **kwargs):
print("before Execution")
# getting the returned value
returned_value = func(*args, **kwargs)
print("after Execution")
# returning the value to the original frame
return returned_value
return inner1
# adding decorator to the function
#hello_decorator
def sum_two_numbers(a, b):
print("Inside the function")
return a + b
a, b = 1, 2
# getting the value through return of the function
print("Sum =", sum_two_numbers(a, b))
You can rewrite this code as
def limit_decorator(func):
def internal(arg):
if (arg >= 0):
return func(arg)
else:
raise Exception("false input")
return internal
#limit_decorator
def square_root(a):
return a * 0.5
a = -5
print("Sum =", square_root(a))
I would suggest to do the validation of x, using one layer on nested functions (basically merge the validator function into the decorator)
def deco(f):
def wrapper(x):
if x<=0:
return False
else:
return f(x)
return wrapper
#deco
def f(x):
return x**0.
f(1) #returns false
f(4) #returns 2.0
Try this:
def decorator(validator):
def subdecorator(function):
def actual_function(arg):
if not validator(arg):
raise ValueError(f"Bad data: {arg}")
return function(arg)
return actual_function
return subdecorator

Combination Lock Program

Me and my partner have been working on this for a few hours and can't figure this out. The directions are vague in some areas and our professor did not do a good job of breaking it down to help us. Here is a link to the directions. I believe they are not very clear but please correct me if I am wrong and just overthinking it https://imgur.com/a/huHnwos
I believe that our biggest problems are the unlock(combination) and set_new_combination(new_combination) methods. I can figure out the str() method as that one isn't very hard to do. We've tried the things our professor has told us to try but they have been unsuccessful.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if combination == True or combination == 0:
self.locked = False
def set_new_combination(self, new_combination):
if self.locked == False:
self.combination = new_combination
def is_locked(self):
if self.locked == True or self.combination == True:
return True
else:
return False
def __eq__(self, other):
if other is not None and type(other) == type(self):
if self.combination == other.new_combination:
return False
def __str__(self):
return self.combination, ',', self.locked
The expected result should be a working basic combination lock.
Here's my implementation based on the inctructions provided, with comments where it deviates from your code.
class Lock:
def __init__(self, combination = 0): # No change here
self.combination = combination
self.locked = False
def lock(self):
# Although a test of self.locked is redundant, the instructions state
# "...if invoked a second time this, method should do nothing."
if not self.locked:
self.locked = True
def unlock(self, combination):
# You were not testing the stored combination against the one passed to the method.
# it does not matter if the stored combination is zero or a different number,
# you still need to check for equality.
# You also need a test as with lock() to satisfy the "if invoked a second time this,
# method should do nothing" requirement.
if self.locked and self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
# You can simply the `if` condition, there's no need to test against False
if not self.locked:
self.combination = new_combination
def is_locked(self):
# I don't know why you are testing the self.combination value, you
# only need to return the state of the lock
return self.locked
def __eq__(self, other):
# You have the correct guard conditions but were returning False when
# the combinations matched. You can simply return the comparison result.
if other is not None and type(other) == type(self):
return self.combination == other.new_combination
def __str__(self):
# For some reason the output format specified for this appears to put it in a list
# (the square brackets) but as it's only for display we'll "fake" the list.
# The `if` statement prints the word 'locked' or 'unlocked' depending on the
# `self.locked` state.
return '[{}, {}]'.format(self.combination, 'locked' if self.locked else 'unlocked')
There are couple of problems with your code. First, if statement in your unlock method will be executed only if combination == 0 or combination == 1, which has nothing to do with lock's combination (self.combination). In your is_locked method you should only return self.locked, no need for if. __eq__ method can also be simplified. And __str__ method should actually return String.
class Lock:
def __init__(self, combination = 0):
self.combination = combination
self.locked = False
def lock(self):
self.locked = True
def unlock(self, combination):
if self.combination == combination:
self.locked = False
def set_new_combination(self, new_combination):
if not self.locked:
self.combination = new_combination
def is_locked(self):
return self.locked
def __eq__(self, other):
return isinstance(other, Lock) and self.combination == other.combination
def __str__(self):
return f'{self.combination}, { "locked" if self.locked else "unlocked"}'
Your unlockmethod is trying to compare a boolean to a number (the combination). Change it to look like this:
def unlock(self, combination):
if combination == self.combination:
self.locked = False
You also did this in your is_locked method, so that should be changed too:
def is_locked(self):
return self.locked
(Any time you find yourself writing something along the lines of if x return True else return False you can almost always replace this with return x if the conditional is simple).
set_new_combination works fine; I don't know what issue you saw with it.
Finally, your __str__ method should actually return a string:
def __str__(self):
return '[' + str(self.combination) + ', ' + 'locked' if self.locked else 'unlocked' + ']'

Decorating all functions in a file if variable is set to true in python

I have the code
chop_flag = True
def decorator(func):
def chop_num(num):
num = str(num)
num = num[:15]
return float(num)
return chop_num
def decorator2():
return decorator if chop_flag else break
And then before each function in my program I add #decorator2. This is producing a type error ("decorator2() takes 0 positional arguments but 1 was given") and I'm also pretty sure this is not a great way to do what I'm trying to do.
To summarize: I'm trying to take the output of each function in my program and run it through the chop_num() function if chop_flag = True; if chop_flag = False, I want it to just ignore the decorator. I thought I'd do this by applying a decorator to each function. Any help would be appreciated.
Updated code:
chop_flag = True
def decorator(func):
if chop_flag == True:
def chop_num(num, *args, **kwargs):
num = func(*args, **kwargs)
num = str(num)
num = num[:15]
return float(num)
return chop_num
else:
return
It now runs properly, but when I import my file into the interpreter (for example) and call the add function, it says "add() missing 1 required positional argument" even though I have put in two arguments.
I think your final decorator should look more like this
def chop_num(num):
num = str(num)
num = num[:15]
return float(num)
chop_flag = True
def decorator(func):
def wrapper(*args, **kwargs):
val = func(*args, **kwargs)
if chop_flag:
return chop_num(val)
else:
return val
return wrapper
#decorator
def nineteen_digits():
return 1_000_000_000_000_000_000
print(nineteen_digits())
# 100000000000000.0
chop_flag = False
print(nineteen_digits())
# 1000000000000000000

Recursion and return statements in python

I am trying to implement a 2-3 tree but I am having trouble with the find method.
This method given an int as parameter should return the node that contains the int.
The problem is that sometimes it works, sometimes it does't and I don't know why.
I have added a test print. For a particular int that I know for sure that is part of the tree, the code executes the print statement, meaning that it has found the node, but does not return this node. Instead it return False which is at the end of the code.
Can you help me solving this ?
def find(self,data,node=0): #return problem ???
if node==0:
a=self.root
else:
a=node
if a.data2==None:
if data==a.data: ### here is the problem
print("qwertyuiop") ### it does not execute the return statement
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
else:
if a.data2!=None:
if (data==a.data or data==a.data2):
return a
elif data<a.data:
if a.left!=None:
return self.find(data,a.left)
elif (data>a.data and data<a.data2):
if a.middle!=None:
return self.find(data,a.middle)
elif data>a.data2:
if a.right!=None:
return self.find(data,a.right)
print("Not Found") ### function executes this print
return False
self.root is the root of the tree and is an object of the following class
class Node:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.data2 = None
self.data3 = None
self.left = left
self.right = right
self.middle = None
self.middle2 = None
Binary Search Tree:
class Nodeee:
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
class BST:
def __init__(self, root=None):
self.c=[]
self.total=0
self.root = None
def parent(self,data,node=5):
def search(nodee,cc,data):
if data==cc.data:
return nodee
else:
if data<cc.data:
nodee=cc
return search(nodee,cc.left,data)
elif data>cc.data:
nodee=cc
return search(nodee,cc.right,data)
print("Parent Error")
return False
if node==self.root:
print("Root has no parent")
else:
a=self.root
c=self.root
return search(a,c,data)
def lookup(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data < a.data:
if a.left==None:
return a
else:
return self.lookup(data,a.left)
elif data > a.data:
if a.right==None:
return a
else:
return self.lookup(data,a.right)
def find(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
print("WTF")
return a
elif data < a.data:
if a.left!=None:
return self.find(data,a.left)
elif data > a.data:
if a.right!=None:
return self.find(data,a.right)
print("Not Found")
return False
def find2(self,data,node=0):
if node==0:
a=self.root
else:
a=node
if data==a.data:
return True
elif data < a.data:
return self.find2(data,a.left)
elif data > a.data:
return self.find2(data,a.right)
return False
def is_empty(self):
if self.root==None:
return True
def is_leaf(self,n):
if (n.left==None and n.right==None):
return True
return False
def delete(self):
self.root=None
def insert(self, data):
if self.root==None:
self.root=Nodeee(data)
self.total+=1
return True
else:
b=self.lookup(data)
if data < b.data:
b.left=Nodeee(data)
self.total+=1
return True
elif data > b.data:
b.right=Nodeee(data)
self.total+=1
return True
print("Insert Error !")
return False
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
def remove(self,data):
a=self.find(data)
if self.is_leaf(a)==True:
b=self.parent(data)
if b.left==a:
b.left=None
elif b.right==a:
b.right=None
elif self.is_leaf(a)==False:
if a.left==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.right
elif b.right==a:
b.right=b.right.right
elif a.right==None:
b=self.parent(data)
if b.left==a:
b.left=b.left.left
elif b.right==a:
b.right=b.right.left
elif (a.left!=None and a.right!=None):
self.inorder_swap(data)
self.remove(data)
def inorder(self,node):
if node!=None:
self.inorder(node.left)
self.c.append(node.data)
self.inorder(node.right)
def inorder_print(self):
self.c=[]
self.inorder(self.root)
print("\nStart")
for x in range(len(self.c)):
print(self.c[x], end=",")
print("\nFinish\n")
a=BST()
print(a.insert(234)==True)
print(a.insert(13)==True)
print(a.insert(65)==True)
print(a.insert(658)==True)
print(a.insert(324)==True)
print(a.insert(86)==True)
print(a.insert(5)==True)
print(a.insert(76)==True)
print(a.insert(144)==True)
print(a.insert(546)==True)
print(a.insert(2344)==True)
print(a.insert(1213)==True)
print(a.insert(6345)==True)
print(a.insert(653348)==True)
print(a.insert(35324)==True)
print(a.insert(8463)==True)
print(a.insert(5555)==True)
print(a.insert(76539)==True)
print(a.insert(14499)==True)
print(a.insert(59999946)==True)
a.inorder_print()
a.remove(35324)
a.remove(1213)
a.remove(2344)
a.remove(144)
a.remove(5555)
a.remove(6345)
a.remove(59999946)
a.remove(76)
print(a.root.data)
a.inorder_print()
def inorder_swap(self,data):
a=self.find(data)
b=a.right
while self.is_leaf(b)!=True:
if b.left!=None:
b=b.left
elif b.left==None:
b=b.right
temp=a.data
a.data=b.data
b.data=temp
a here is the node containing the passed data. This method does nothing else than swapping a's data with some leaf's data (first one while finds), thereby distorting the tree order. A follow-up find on the same data therefore fails and returns False. Since your code has no error checks, this results in an AttributeError.
You probably want to move nodes around in inorder_swap. However you only assign to the local name b. If you want to change nodes, then you need to use b.left = or b.right =.
It might be that there are more problems, that I don't see right now.
Also your code has several style problems, some of them:
You have four functions doing the same: find, find2, lookup and search in parent.
Most of the naming is not informative or even confusing.
Lines like if a.right==None: should be written as if not a.right: (or maybe if a.right is None:).
Check the return value of functions and don't just assume they return a valid node if they might not (i.e. find might return False instead of a node). Or alternatively use exception handling.
If you have an if ... elif ... elif block you don't have to check the last possibility if it is sure to be true, for example:
if b.left!=None:
# something
elif b.left==None:
# something else
should be
if b.left:
# something
else:
# something else

Categories

Resources