I'm trying to create collection filters from expression trees (these would be generated from the GUI using wxpython tree controls). I would then use these filters with python's filter(func, iterable) method.
The challenge now is how can I create a function at runtime based on the rules found in the expression tree. An example of how such a function would look is:
def filterFunc(element):
if element == 'Apple' or element == 'Orange' or element == 'Duck':
return True
return False
The solution I'm currently thinking is to traverse the tree, generate a string containing actual Python code based on the tree contents(probably painful to code), and then call eval() on the resulting string.
Any advice or pointers on what would the correct/pythonic way to solve this would be much appreciated !
I'm assuming that your expression tree is composed of a number of objects, whose type corresponds with what kind of expression it is. Ex. Or, Equals, strings, etc. Something like this:
class OrExpression:
def __init__(self, left, right):
self.left = left
self.right = right
class EqualsExpression:
def __init__(self, left, right):
self.left = left
self.right = right
class Literal:
def __init__(self, value):
self.value = value
class Variable:
def __init__(self, name):
self.name = name
An expression equivalent to your example would look like this:
e = OrExpression(
EqualsExpression(
Variable("element"),
Literal("Apple")
),
OrExpression(
EqualsExpression(
Variable("element"),
Literal("Orange")
),
EqualsExpression(
Variable("element"),
Literal("Duck")
)
)
)
You could create a method eval for each class that evaluates itself for a given context. Like so:
class OrExpression:
def __init__(self, left, right):
self.left = left
self.right = right
def eval(self, variables):
return self.left.eval(variables) or self.right.eval(variables)
class EqualsExpression:
def __init__(self, left, right):
self.left = left
self.right = right
def eval(self, variables):
return self.left.eval(variables) == self.right.eval(variables)
class Literal:
def __init__(self, value):
self.value = value
def eval(self, variables):
return self.value
class Variable:
def __init__(self, name):
self.name = name
def eval(self, variables):
return variables[self.name]
Then you can call eval and supply the context. In your example, you only need to pass in the value of element.
print e.eval({"element": "Apple"})
print e.eval({"element": "Duck"})
print e.eval({"element": "Banana"})
Result:
True
True
False
But what if, instead, you don't differentiate kinds of expression by type? Suppose your tree is composed of plain old nodes, that identify what kind of expression they are using their value attribute. The code is approximately the same, just using a single monolithic switch case, instead of individual eval methods.
class Node:
def __init__(self, value=None, *children):
self.value = value
self.children = children
def evalTree(t, variables):
if t.value == "Or":
return evalTree(t.children[0], variables) or evalTree(t.children[1], variables)
elif t.value == "Equals":
return evalTree(t.children[0], variables) == evalTree(t.children[1], variables)
elif t.value == "Literal":
return t.children[0].value
elif t.value == "Variable":
name = t.children[0].value
else:
raise Exception("Unrecognized node type")
t = Node("Or",
Node("Equals",
Node("Variable", Node("element")),
Node("Literal", Node("Apple"))
),
Node("Or",
Node("Equals",
Node("Variable", Node("element")),
Node("Literal", Node("Apple"))
),
Node("Equals",
Node("Variable", Node("element")),
Node("Literal", Node("Apple"))
)
)
)
print evalTree(t,{"element": "Apple"})
print evalTree(t,{"element": "Duck"})
print evalTree(t,{"element": "Banana"})
Result:
True
True
False
Related
So I'm working on an assignment for a data structures class, and one question is to create a put(key, value) method for an AVLTree. I know I have to adding the balancing method, but right now I'm just on the actual insertion. I want the function to work where you can create a tree and type newTree.put(key value) and have it work. Right now I have
class node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
self.height = 0
class AVLTreeMap:
def __init__(self, key, value):
self.root = node(key, value)
#inserting new key-value pair, NEED TO ADD BALANCING ABILITY
def put(self, key, value, height=0):
if(key < self.root.key):
if(self.root.left == None):
self.root.left = node(key, value)
self.root.left.height = height+1
else:
self.root = self.root.left
self.put(key, value, height+1)
else:
if(self.root.right == None):
self.root.right = node(key, value)
self.root.right.height = height+1
else:
self.root = self.root.right
self.put(key, value, height+1)
However, the recursive aspect of put just discounts the root, and creates a new tree of just one parent and that node as one child. Is this the right way to go about this, or is there an easier way? Also, if I do it this way, how do you recurse left and right in this method?
I am trying to traverse a tree but getting above error. Please help me out. I am trying to call a definition of same class and as parameter sending a class object. But calling definition not able to identify type of parameters.
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 same(self, s, t):
if(s is None and t is None):
return True
if(s is None or t is None):
return False
return s.val==t.val and self.same(s.left,t.left) and self.same(s.right,t.right)
def traverse(self, s, t):
return (s!="" and (self.same(s,t) or self.traverse(s.left,t) or self.traverse(s.right,t)))
def isSubtree(self, s, t):
# print s.val
return self.traverse(s,t)
The problem looks like it is the '' check as opposed to a None check in traverse. It will pass None values to same because of this.
Assuming that t and s are TreeNode objects, where do you declare them? Is it possible that you're passing undeclared variables to Solution?
I've got a binary search tree full of objects. I'm traversing the tree using a callback function that adds a property of all the objects to a global variable. I've got this working, but I'd like to find a way to accomplish this without using a global.
Here's the relevant code:
TOTAL_AGE = 0.0
class Node(object):
def __init__(self, data):
self.left = None
self.right = None
self.data = data
class Tree(object):
def __init__(self):
self.root = None
self.size = 0
def traverse(self, callback):
self._traverse(callback, self.root)
def _traverse(self, callback, node):
if node is None:
return
self._traverse(callback, node.left)
callback(node.data)
self._traverse(callback, node.right)
def add_ages(tree):
tree.traverse(callback)
def callback(student):
global TOTAL_AGE
TOTAL_AGE += student.age
def main():
tree = bst.Tree()
add_ages(tree)
print TOTAL_AGE
This is admittedly for an assignment, which requires that I use the current traverse function and not a different implementation. That's mainly my issue though because I don't see a way to do this without using a global or modifying traverse().
Thanks in advance for any help.
You could pass a method of a class instance as callback so that you can keep track of the state in the instance:
class Count(object):
def __init__(self):
self.total_age = 0
def callback(self, student):
self.total_age += student.age
And then instantiate Count and pass its callback method to the Tree:
count = Count()
tree.traverse(count.callback)
I am very new to python and need some help with instantiating an object. The python interpreter is giving me trouble when instantiating an object of a class I defined. There are two classes, BTNode and BST (which are stored in files bst_node.py and bst.py respectively):
# file: bst_node.py
class BTNode:
"""a binary search tree node implementation"""
def ___init___(self, value):
self.value = value
self.left is None
self.right is None
self.parent is None
def ___init___(self, value, left, right, parent):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
def is_leaf(self):
"""check whether this node is a leaf"""
if self.left.value is None and self.right.value is None:
return True
return False
# file: bst.py
from bst_node import *
class BST:
"""a binary search tree implementation"""
def ___init___(self, value):
self.root = BTNode(value)
def insert(self, curRoot, newValue):
if curRoot.is_leaf():
if newValue < curRoot.value:
newNode = BTNode(newValue, None, None, curRoot)
curRoot.left = newNode
else:
newNode = BTNode(newValue, None, None, curRoot)
curRoot.right = newNode
else:
if newValue < curRoot.value:
self.insert(curRoot.left, newValue)
else:
self.insert(curRoot.right, newValue)
So, in the interpreter I do:
import bst as b
t1 = b.BST(8)
and I get an error which says that this constructor takes no arguments
The constructor clearly takes an argument value so what is going wrong here? How can I fix this error?
Thanks, all help is greatly appreciated!
The first issue is that you called your functions ___init___ instead of __init__. All of the 'special methods' use two underscores.
A second issue in this code is that in BTNode you redefined __init__. You can't overload functions in python. When you reclare __init__ you effectively deleted the first constructor.
A third issue is your usage of is. is is an operator that checks whether two objects are exactly the same and returns True or False. In the constructor, you have a few self.left is None is examining the value of self.left (which wasn't declared yet), and examining whether or not it is None. To set it, use = as follows:self.left = None
To fix the second and third issue you should use default argument values. For example:
def __init__(self, value, left=None, right=None, parent=None):
In addition to the number of underscores problem, you should replace
def ___init___(self, value):
self.value = value
self.left is None
self.right is None
self.parent is None
def ___init___(self, value, left, right, parent):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
with
def __init__(self, value, left=None, right=None, parent=None):
"""set the parameters to corresponding class members"""
self.value = value
self.left = left
self.right = right
self.parent = parent
Because as #Moshe points out, you can't overload functions, you should use default arguments insted.
Changing ___init___ to __init__ should fix it. (2 underscores vs 3)
I'm trying to build a very lightweight Node class to serve as a Python-based hierarchy search tool. See the definition below.
from functools import reduce
from operator import or_
class Node:
def __init__(self, name):
self.name = name
self.children = []
def add_child(self, child_node):
self.children.append(child_node)
def contains(self, other_node):
if self == other_node:
return True
elif other_node in self.children:
return True
else:
return reduce(or_, [child.contains(other_node)
for child in self.children], False)
def is_contained_by(self, other_node):
return other_node.contains(self)
def __eq__(self, other_node):
return self.name == other_node.name
def __de__(self, other_node):
return self.name != other_node.name
contains seems to be a textbook case of functional programming (pulled directly from Why Functional Programming Matters).
Question: is there a more efficient or Pythonic way of writing contains? I know that map is usually replaced by list comprehension, but I hadn't seen a better way of doing reduce-based recursion.
Thanks,
Mike
===EDITED ... HERE'S THE REDONE CLASS TAKING INTO ACCOUNT THE ANSWER AND COMMENTS===
class Node:
def __init__(self, name):
self.name = name
self.children = []
def add_child(self, child_node):
# Hattip to lazyr for catching this.
if self.contains(child_node) or child_node.contains(self):
raise TreeError('A relationship is already defined.')
else:
self.children.append(child_node)
def contains(self, other_node):
# Hattip to lazyr for pointing out any() and to Jochen Ritzel for
# eliminating the silly child check.
return (self == other_node or
any(child.contains(other_node) for child in self.children))
def is_contained_by(self, other_node):
return other_node.contains(self)
def __eq__(self, other_node):
return self.name == other_node.name
def __de__(self, other_node):
return self.name != other_node.name
def __repr__(self):
return self.name
I think (not tested) that you instead of reduce should use any like this, which will stop on the first hit:
return any(child.contains(other_node) for child in self.children)
By the way, did you mean for a.contains(b) to return False when a == b and len(a.children) > 0?
Edit: If your tree contains a loop, like this:
a = Node("a")
b = Node("b")
a.add_child(a)
a.add_child(b)
then
a.contains(b)
will crash the program. You may want to check for this either in contains or in add_child, depending on which you use the most.