Dynamic list comprehension - python

I would like to know if python is able to create a list by comprehension using multiple and optional criteria.
Let's make an example. Considering the following object (partial description):
class Person():
def __init__(self):
self.id = <next id of some kind>
self.name = 'default name'
self.gender = 'm'
self.age = 20
<...>
Suppose I created a list of all Persons into world. Then I want to create a GUI which will allow me to browse the collection based on search criteria (the GUI conception is out of scope of the question), e.g name (regex based), id, gender and age (with equal, not equal and greater or lesser than). None of the search criteria are mandatory (we can suppose it's None I guess) and the type do not really matter for this question.
How can I filter the list of Person in a clever python-way?
If I have known criteria I could do a comprehension :
l = [person for person in world if re.search(person.name, '.*Smith') and person.gender = 'm' and person.age < 20]
But as the user is able to choose what he wants, I won't know what criteria to use. I can of course build this as fully fledged function:
l = world
if nameSearch:
l = [person for person in l if re.search(person.name, nameSearch)]
if genderSearch:
l = [person for person in l if gender == genderSearch]
<...>
return l
But I feel python would have a way to do it more properly.

Based DCS' comment, here is a example how to use functions as filters. A filter is just a function which returns a boolean (given an instance of Person). For faster processing I suggest you take a look at pandas, which is a very good choice for data filtering/sorting/munging, but this might get you started with a simple solution. The only task that is left to you, is to create the filters based on the user's input.
from random import random
class Person():
def __init__(self, id):
self.id = id
self.name = 'Name{}'.format(id)
self.gender = 'm' if random() > 0.5 else 'f'
self.age = int(random() * 10) + 10
def __repr__(self):
return 'Person-{} ({}, {}. {})'.format(self.id,
self.name,
self.gender,
self.age)
Setting up some test data:
people = [Person(id) for id in range(10)]
[Person-0 (Name0, f. 15),
Person-1 (Name1, f. 14),
Person-2 (Name2, f. 12),
Person-3 (Name3, f. 18),
Person-4 (Name4, m. 12),
Person-5 (Name5, f. 18),
Person-6 (Name6, f. 15),
Person-7 (Name7, f. 15),
Person-8 (Name8, f. 10),
Person-9 (Name9, m. 16)]
Output:
def by_age(age):
return lambda person: person.age == age
def by_name(name):
return lambda person: re.search(person.name, name)
def by_gender(gender):
return lambda person: person.gender == gender
filters = (by_age(15),
by_gender('f'))
filtered_people = (p for p in people if all([f(p) for f in filters]))
list(filtered_people)
Which gives us the following filtered list of people:
[Person-0 (Name0, f. 15), Person-6 (Name6, f. 15), Person-7 (Name7, f. 15)]
You could even change the predicate all to any in order select all people which match any of the specified filters.

Elaborating my comment above:
As functions are first class citizens in Python, you could write a bunch of matcher functions, put them (dynamically) in a list and match against them in a single list comprehension.
Let predicates be a list of one-argument functions of type Person -> bool.
Then simply do:
[ pers for pers in world if all([f(pers) for f in predicates]) ]
Further exploring the functional route of thinking, you can create "dynamic matching functions" by creating functions returning matching functions:
def age_matcher(age):
return lambda p: p.age > age
An age_matcher(someAge) can be added to your predicates array.
Side note
For these "database-search"-like tasks, you will probably want to really should look at libraries like Pandas, where you can make queries similar to SQL. You may be re-inventing a fairly complex type of wheel.

How about this?
def search(self, condition):
return filter(condition, self.l)
def search_re(self, **kwargs):
filters = []
for key, value in kwargs.items():
if isinstance(value, str):
value = re.compile(value)
filters.append(lambda x: re.search(getattr(x, key), value))
elif callable(value):
filters.append(lambda x: value(getattr(x, key)))
else:
filters.append(lambda x: getattr(x, key) == value)
def condition(person):
return all(
f(person) for f in filters
)
return self.search(condition)
Usage:
persons.search(lambda x: x.name == "bla")
persons.search_re(name=".*Smith", gender="male")

Related

python list dependency based on other value in same list

I am struggling to figure how to make dependency b/w values in python list.
Basically I have a list like below. from the below list ,i can pass a input value as TABLE_VIEW
based on the input value , i want to generate dependency list in a order.
INPUT
1.EXP_TABLE_NAME_STG,TARGET_TABLE
2.SQ_TABLE_NAME,EXP_TABLE_NAME_STG
3.TABLE_VIEW,SQ_TABLE_NAME
4.EXP_TABLE_NAME_STG,LKP_NEW_TABLE_3
5.SQ_TABLE_NAME,LKP_NEW_TABLE_1
6.EXP_TABLE_NAME_STG,LKP_NEW_TABLE_2
7.LKP_NEW_TABLE_1,TARGET_TABLE
For example 3rd one value is TABLE_VIEW,SQ_TABLE_NAME, so here based on 2nd value i.e SQ_TABLE_NAME I want to find out next dependency so in this case
SQ_TABLE_NAME,EXP_TABLE_NAME_STG
SQ_TABLE_NAME,LKP_NEW_TABLE_1
again from above two , take the 2nd value and again make dependency.
EXP_TABLE_NAME_STG,LKP_NEW_TABLE_3
EXP_TABLE_NAME_STG,LKP_NEW_TABLE_2
EXP_TABLE_NAME_STG,TARGET_TABLE
LKP_NEW_TABLE_1,TARGET_TABLE
I may have up to 50 list like this, but wanted to put them in dependency order based on 2nd value.
OUTPUT:
1.TABLE_VIEW,SQ_TABLE_NAME
2.SQ_TABLE_NAME,EXP_TABLE_NAME_STG
3.SQ_TABLE_NAME,LKP_NEW_TABLE_1
4.EXP_TABLE_NAME_STG,LKP_NEW_TABLE_3
5.EXP_TABLE_NAME_STG,LKP_NEW_TABLE_2
6.EXP_TABLE_NAME_STG,TARGET_TABLE
7.LKP_NEW_TABLE_1,TARGET_TABLE
I have tried writing static query by taking multiple list variable and deleted already processed one original list, but I many never know when all values ends up. Can you please share some thoughts how to implement this dynamically?
sq_order_dependency=[]
for sq_dep in job_dependent_details:
if 'SQ' in sq_dep.split(',')[0] :
sq_order_dependency.append(sq_dep)
job_dependent_details.remove(sq_dep)
sq_order_dependency1=[]
for sq_depenent_order in sq_order_dependency:
next_dependency=sq_depenent_order.split(',')[1]
#print(next_dependency)
for job_dependent_details_list in job_dependent_details:
if next_dependency in job_dependent_details_list.split(','[0]):
#print(job_dependent_details_list)
sq_order_dependency.append(job_dependent_details_list)
for i in sq_order_dependency:
job_dependent_details.remove(i)
I would follow a different approach, i.e. build a tree-like structure of dependency pairs, and then print out the tree.
In the following code I defined a simple Dep class and chose a depth first traversal for showing the tree, both for readability; and since we meet the dependencies in an unspecified order, I used a helper dictionary. Oh, and I abbreviated the table names out of laziness :)
class Dep():
def __init__(self, name, children = None):
self.name = name
if children:
self.children = [children]
else:
self.children = []
def add_child(self, child):
self.children.append(child)
def show(self, level=0):
for c in self.children:
print ('\t'*level, self.name, c.name)
c.show(level+1)
def show_dependencies(deps):
out = {}
root = deps[0][0]
for d in y:
pname, cname = d
if cname in out:
c = out[cname]
else:
c = Dep(cname)
out[cname] = c
if pname in out:
out[pname].add_child(c)
else:
out[pname] = Dep(pname, c)
if root == cname:
root = pname
out[root].show()
>>> show_dependencies([('EXP','TARGET'),('SQ','EXP'),('TABLE','SQ'),('EXP','LKP3'),('SQ','LKP1'),('EXP','LKP2'),('LKP1','TARGET')])
TABLE SQ
SQ EXP
EXP TARGET
EXP LKP3
EXP LKP2
SQ LKP1
LKP1 TARGET
well according to your examples and data there should be no duplicates so it should work i think.
main function is based on recursive call (something like dfs)
this works only for directed edges and without self-node edge.
from collections import defaultdict
a=['EXP_TABLE_NAME_STG, TARGET_TABLE'
,'SQ_TABLE_NAME, EXP_TABLE_NAME_STG'
,'TABLE_VIEW, SQ_TABLE_NAME'
,'EXP_TABLE_NAME_STG, LKP_NEW_TABLE_3'
,'SQ_TABLE_NAME, LKP_NEW_TABLE_1'
,'EXP_TABLE_NAME_STG, LKP_NEW_TABLE_2'
,'LKP_NEW_TABLE_1, TARGET_TABLE']
a = [i.replace(" ",'') for i in a]
sql_data = [tuple(i.split(',')) for i in a]
class CustomGraphDependency:
#desired for question
def __init__(self,data:list):
self.graph = defaultdict(set) # no self edge
self.add_dependency(data)
self.count = 1
def add_dependency(self,data:list):
for node1,node2 in data: #directed edge only
self.graph[node1].add(node2)
def dependency_finder_with_count(self,node: str, sq_order_dependency: list, flag: list):
# (e.x 1.TABLE_VIEW,SQ_TABLE_NAME )
flag.append(node)
for item in self.graph[node]:
sq_order_dependency.append((self.count,node,item))
if item not in flag:
self.count+=1
self.dependency_finder_with_count(item, sq_order_dependency, flag)
return sorted(sq_order_dependency,key=lambda x: x[0])
obj_test = CustomGraphDependency(sql_data).dependency_finder_with_count('TABLE_VIEW', [], [])
for i in obj_test:
print(i)
'''
(1, 'TABLE_VIEW', 'SQ_TABLE_NAME')
(2, 'SQ_TABLE_NAME', 'EXP_TABLE_NAME_STG')
(3, 'EXP_TABLE_NAME_STG', 'LKP_NEW_TABLE_3')
(4, 'EXP_TABLE_NAME_STG', 'TARGET_TABLE')
(5, 'EXP_TABLE_NAME_STG', 'LKP_NEW_TABLE_2')
(6, 'SQ_TABLE_NAME', 'LKP_NEW_TABLE_1')
(7, 'LKP_NEW_TABLE_1', 'TARGET_TABLE')
'''

What could be a better alternative to this For?

I'm a beginner trying to write up a game about trading in Python 3.x.
I have two classes (Depot and Good). Instances of the class Good are stored in a dictionary inside depot.inv (the instance as Key, and the amount of it as Value). When the user is asked to write what does he want to take, he will write the name of the instance ('Iron' instead of ir). So I took a For loop and searched through everything the Depot had in it's inventory. If he finds that the reply of the user is = to the name of any instance (ir.name, for example), then the program gains access to the instance as a whole.
The question is, how can I do this without a For-Loop?
I imagine that searching the whole inventory each time a reply is made is not optimal, even less if it's a linear search.
class Depot:
def __init__ (self, name, inv, bank):
self.name = name
self.inv = inv
self.bank = bank
class Good:
def __init__(self, name, weight, rare):
self.name = name
self.weight = weight
self.rare = rare
ir = Good('Iron', 1, 0.1)
gd = Good('Gold', 0.4, 2)
sl = Good('Silver', 0.7, 6.3)
mars = Depot('Mars', {ir: 10500, gd: 800, sl: 6000}, 1000)
player = {ir: 100}
reply = input('What do you want to take?')
i, q = reply.split()
for k in mars.inv.keys(): #This is the crux of the problem
if i in k.name:
x = k
print('Got it')
if x in mars.inv:
if int(q) > mars.inv[x]:
print('We dont have that much.')
elif int(q) <= mars.inv[x]:
mars.inv[x] -= int(q)
if x in player:
player[x] += int(q)
elif i not in player:
player[x] = int(q)
Using list comprehension and next() allows you to only find the first occurance of the item you're looking for, and then stop. Breaking the for loop after finding the key you're looking for would achieve the same thing. Both are O(n) there isn't really that much room to optimize. A one liner would be
x = next((k for k in mars.inv.keys() if i in k.name), None)
I'd probably duplicate the name as the key of the dictionary and map it to a tuple of the other information:
{"Iron": (ir, 10500), "Gold": (gd, 800), "Silver": (sl, 6000}
Then you can do mars.inv["Iron"] to get the tuple of (ir, 10500), and you can extract whichever data you want from that.
Note though, this requires an exact match on the name. You're currently using in to do the check, which will prevent any optimizations. Since from your description, you're doing lookups based on exact names (ignoring case, since that can be easily accounted for), in is unnecessary.

Sort list with information from text file

I have been reading class objects from a text file and stored the information in a list. My question is: How can I sort the list in a numerical order.
Text file:
Hugo 10
Isac 9
John 90
Kelly 8
Code:
class A:
def __init__(self,name, age):
self.name = name
self.age = age
def sort_list():
a = list()
file = open('my_textfile.txt', 'r').readlines()
for k in file:
k = k.split(' ')
af = A(k[0], k[1])
a.append(af)
return a
Then I want to sort my list so that both columns get sorted numerically. Like this:
Kelly 8
Isac 9
Hugo 10
John 90
You most likely have found a solution now. If not, let me suggest something.
First, I made a slight modification to your class: I added a string representation that makes prints more readable:
class A:
def __repr__(self):
return f"A('{self.name}', {self.age})"
def __init__(self, name, age):
self.name = name
self.age = age
You don't need that for the following!
Now the sorting:
def sort_list():
lst = list()
lines = open('my_textfile.txt', 'r').readlines()
for line in lines:
line = line.split()
lst.append(A(line[0], int(line[1])))
lst.sort(key=lambda a: (a.age, a.name))
return lst
Apart from renaming some variables I have made the following adjustments:
I converted the second part of the strings to an int because I think that's what you actually want (int(line[1])).
After appending all the data to the list it get's sorted. Lists have a sort method, but in your case you have to give it some hints on how to sort, since the elements are a new class. The hint is the lambda function given to the optional key argument of sort. It returns for every element of the list the tuple (a.age, a.name). sort then actually sorts according to this values. Since the age is first, the sorting looks a it first and the result is an increasing age in the final list. For entries with same age the sorting proceeds in alphabetical order on the names.
The result of
lst = sort_list()
print(lst)
is
[A('Kelly', 8), A('Isac', 9), A('Hugo', 10), A('John', 90)]
Here is one solution using the code you suggest. Break the data into two parts sort it and put it back together again. There are other ways of doing this with pandas.
class A:
def __init__(self,name, age):
self.name = name
self.age = age
def sort_list():
a = list()
name = list()
data = list()
file = open('my_textfile.txt', 'r').readlines()
for k in file:
k = k.split(' ')
name.append(k[0])
data.append(k[1])
name.sort()
data.sort()
length = len(name)
for i in range(length):
af = A(name[i], data[i])
a.append(af)
return a

Using lambda function to change value of an attribute

Can I use lambda function to loop over a list of class objects and change value of an attribute (for all objects or for the one that meet a certain condition)?
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
student1 = Student('StudOne',18)
student2 = Student('StudTwo',20)
student3 = Student('StudThree',29)
students = [student1,student2,student3]
new_list_of_students = map(lambda student:student.age+=3,students)
Unfortunately, that’s not possible since the body of a lambda only allows for simple expressions while a student.age += 3 is a statement. So you can’t use a lambda there. You could however still use the map solution:
def incrementAge (student):
student.age += 3
return student
students2 = map(incrementAge, students)
Note that students2 will contain the same students as students though, so you don’t really need to capture the output (or return something from incrementAge). Also note that in Python 3, map returns a generator which you need to iterate on first. You can call list() on it to do that: list(map(…)).
Finally, a better solution for this would be to use a simple loop. That way, you don’t have overhead of needing a function or create a duplicate students list, and you would also make the intention very clear:
for student in students:
student.age += 3
Using a simple for-loop to retrieve the students to update the age for each is good enough like others said, but if you still want to use a lambda to update the values, you may need to leverage the exec() function:
_ = list(map(lambda student: exec("student.age+=3"), students))
for _ in students: print(_.age)
Output:
21 23 32
In this case, what actually does the updating is the exec(), and the map() just yields None. So the returned result makes no sense and I put a _ to clarify this. A more concise form would be this:
list(map(lambda _: exec("_.age+=3"), students))
Besides, if only considering what you want to do, you don't need to use a map() at all (probably more confusing though):
[(lambda _: exec("_.age += 3"))(_) for _ in students]
Furthermore, a lambda can be discarded either:
[exec("_.age += 3") for _ in students]
As you can see, no "trick" codes above seem more concise than what other answers post:
for s in students:
s.age += 3
So maybe the so-called "one-liner" is useful just when it comes to having fun... :)
Lambda functions can only contain expressions, not statements. Assignment in Python is a statement. Lambdas cannot do assignments. Additionally, assignment statements do not evaluate to their values, so your map would not produce a list of students.
You want this:
for student in students:
student.age += 3
This does not give you a new list, it modifies the old list, but your old list would be modified anyway, you aren't doing anything to produce new Students.
You can use setattr, which will apply the change to the objects. A big plus is that you can continue using the same list.
map(lambda s: setattr(s, 'age', s.age + 3), students)
From the docs:
The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.
The equivalency of which is:
for s in students:
s.age += 3
If you really want a new list:
The above approach doesn't return a new list; instead returning None (the return value of setattr). Adding an or comparison with the object you want in the array (in this case s) will amend that, though.
new_students = map(lambda s: setattr(s, 'age', s.age + 3) or s, students)
The comparison is equivalent to None or s which will always yield the latter. Also note that the new list is identical to the old one.
Have you tried playing around with the setattr and getattr functions? With these you can write and read the writable attributes of the object directly.
So you could do something like this:
map_of_students = map(lambda student: setattr(student, "age", getattr(student, "age") + 3), students)
print("Age BEFORE list: " + str(students[0].age)); list(map_of_students); print("Age AFTER list: " + str(students[0].age))
In this case the original students' list will be updated with the new ages for each student, which might not be necessarily what you want, but can easily be worked around by making a backup of the original list's objects before casting the map object to a list. In that case, you could do:
students_bkp = []
for s in students:
students_bkp.append(Student(**s.__dict__))
Here is the full snippet:
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
student1 = Student('StudOne',18)
student2 = Student('StudTwo',20)
student3 = Student('StudThree',29)
students = [student1,student2,student3]
students_bkp = []
for s in students:
students_bkp.append(Student(**s.__dict__))
map_of_students = map(lambda student: setattr(student, "age", getattr(student, "age") + 3), students)
print("Age BEFORE list: " + str(students[0].age)); list(map_of_students); print("Age AFTER list: " + str(students[0].age)); print("Age BKP: " + str(students_bkp[0].age))
Q: "Can I use lambda function to loop over a list of class objects and change value of an attribute"
A: Yes....but you shouldn't. It's poor coding style, inefficient, and only appropriate for things like code golf
You should write it like the other two answers have suggested.
...but if you really wanted to...
new_list_of_students = [(lambda student:(setattr(student, 'age', student.age+3),
student))(s)[1] for s in students]
print [student.age for student in new_list_of_students]
Prints:
[21, 23, 32]
...or even:
from operator import itemgetter
new_list_of_students = map(itemgetter(1),map(lambda student:(setattr(student,
'age', student.age+3), student),students))
print [student.age for student in new_list_of_students]
[Same output]

Generating evalable python code: all combinations of functions in disjunctive normal form

(A,B,C) = (100, 200, 300)
def f1(p): return p+50
def f2(p): return p*1.5
def f3(p): return p*p
vars_ = (A,B,C)
funcs_ = [f1, f2, f3]
logic_ = ["and","or"]
vol_lmt_ = [200, 300]
op_ = [">","<","="]
I want generate the assert code string for eval() to test the validity, take below one for example:
"f1(A)>200 and f1(B)>200 and f1(C)>200" # True
-^-------------^-------------^------------: funcs_
----^-------------^-------------^---------: vars_
------^-------------^-------------^-------: op_
--------^-------------^-------------^-----: vol_lmt_
------------^-------------^---------------: logic_
My questions are:
how to generate the code string I wanted based on those vars above?
how to enumerate all test logic possibility for above (A,B,C)? For example:
"f1(A)>200 and f1(B)>200 and f1(C)>200"
"f1(A)<300 and f2(B)=200 or f3(C)>200"
is it possible to replace the the name of function to the list entry when generate the code?
"f(A)>200 and f1(B)>200 and f1(C)>200"
To
"funcs_[0](A)>200 and funcs_[0](B)>200 and funcs_[0](C)>200"
This is equivalent to taking the outer/cartesian product, "summing" across the "var" dimension, and interspersing those with the outer product of logic operators. You can use itertools.product or just the normal list comprehensions. The following will work for any number of variables, functions, comparators, logic operators, and numeric thresholds. It is also easily extensible if you choose to make more complicated expressions:
#!/usr/bin/python3
from pprint import pprint as pp
from itertools import *
VARS = 'XYZ'
FUNCS = range(2)
COMPARE = '><='
LOGIC = ['and', 'or']
NUMS = [200, 300]
def listJoin(iter):
return sum(map(list,iter), [])
terms = [
[
'func[{func}]({var}){compare}{num}'.format(func=func, var=var, compare=compare, num=num)
for var in VARS
]
for func in FUNCS
for compare in COMPARE
for num in NUMS
]
def intersperse(iter, joiners):
iter = list(iter)
for tokens in product(*(joiners for _ in iter[:-1])):
yield ' '.join(listJoin(zip(iter,tokens))+[iter[-1]])
formulas = listJoin(intersperse(t, LOGIC) for t in terms)
pp(formulas)
Result:
['func[0](X)>200 and func[0](Y)>200 and func[0](Z)>200',
'func[0](X)>200 and func[0](Y)>200 or func[0](Z)>200',
'func[0](X)>200 or func[0](Y)>200 and func[0](Z)>200',
'func[0](X)>200 or func[0](Y)>200 or func[0](Z)>200',
'func[0](X)>300 and func[0](Y)>300 and func[0](Z)>300',
'func[0](X)>300 and func[0](Y)>300 or func[0](Z)>300',
'func[0](X)>300 or func[0](Y)>300 and func[0](Z)>300',
'func[0](X)>300 or func[0](Y)>300 or func[0](Z)>300',
'func[0](X)<200 and func[0](Y)<200 and func[0](Z)<200',
'func[0](X)<200 and func[0](Y)<200 or func[0](Z)<200',
'func[0](X)<200 or func[0](Y)<200 and func[0](Z)<200',
'func[0](X)<200 or func[0](Y)<200 or func[0](Z)<200',
'func[0](X)<300 and func[0](Y)<300 and func[0](Z)<300',
'func[0](X)<300 and func[0](Y)<300 or func[0](Z)<300',
'func[0](X)<300 or func[0](Y)<300 and func[0](Z)<300',
'func[0](X)<300 or func[0](Y)<300 or func[0](Z)<300',
'func[0](X)=200 and func[0](Y)=200 and func[0](Z)=200',
'func[0](X)=200 and func[0](Y)=200 or func[0](Z)=200',
'func[0](X)=200 or func[0](Y)=200 and func[0](Z)=200',
'func[0](X)=200 or func[0](Y)=200 or func[0](Z)=200',
'func[0](X)=300 and func[0](Y)=300 and func[0](Z)=300',
'func[0](X)=300 and func[0](Y)=300 or func[0](Z)=300',
'func[0](X)=300 or func[0](Y)=300 and func[0](Z)=300',
'func[0](X)=300 or func[0](Y)=300 or func[0](Z)=300',
'func[1](X)>200 and func[1](Y)>200 and func[1](Z)>200',
'func[1](X)>200 and func[1](Y)>200 or func[1](Z)>200',
'func[1](X)>200 or func[1](Y)>200 and func[1](Z)>200',
'func[1](X)>200 or func[1](Y)>200 or func[1](Z)>200',
'func[1](X)>300 and func[1](Y)>300 and func[1](Z)>300',
'func[1](X)>300 and func[1](Y)>300 or func[1](Z)>300',
'func[1](X)>300 or func[1](Y)>300 and func[1](Z)>300',
'func[1](X)>300 or func[1](Y)>300 or func[1](Z)>300',
'func[1](X)<200 and func[1](Y)<200 and func[1](Z)<200',
'func[1](X)<200 and func[1](Y)<200 or func[1](Z)<200',
'func[1](X)<200 or func[1](Y)<200 and func[1](Z)<200',
'func[1](X)<200 or func[1](Y)<200 or func[1](Z)<200',
'func[1](X)<300 and func[1](Y)<300 and func[1](Z)<300',
'func[1](X)<300 and func[1](Y)<300 or func[1](Z)<300',
'func[1](X)<300 or func[1](Y)<300 and func[1](Z)<300',
'func[1](X)<300 or func[1](Y)<300 or func[1](Z)<300',
'func[1](X)=200 and func[1](Y)=200 and func[1](Z)=200',
'func[1](X)=200 and func[1](Y)=200 or func[1](Z)=200',
'func[1](X)=200 or func[1](Y)=200 and func[1](Z)=200',
'func[1](X)=200 or func[1](Y)=200 or func[1](Z)=200',
'func[1](X)=300 and func[1](Y)=300 and func[1](Z)=300',
'func[1](X)=300 and func[1](Y)=300 or func[1](Z)=300',
'func[1](X)=300 or func[1](Y)=300 and func[1](Z)=300',
'func[1](X)=300 or func[1](Y)=300 or func[1](Z)=300']
Maybe this can summaries what you are trying to do (using python2 syntax):
import itertools
arguments = ('A', 'B', 'C', 'D')
funcs_ = [f1, f2, f3, f4]
logic_ = ["and","or"]
op_ = [">","<","="]
vol_lmt_ = [200, 300]
num_func = len(funcs_)
assert num_func == len(arguments), ("The number of argument should be the same as "
"the number of function.")
operands = itertools.product(["funcs_[%d]" % i for i in range(num_func)],
arguments,
op_,
vol_lmt_)
def comp(operands):
templ = "{func}({arg}){op}{val}"
for operand in operands:
yield templ.format(func=operand[0], arg=operand[1],
op=operand[2], val=operand[3])
new_operands = map(comp, itertools.tee(operands, num_func))
# construct the argument to pass to itertools.product.
args = []
for operand in new_operands:
args.append(operand)
args.append(logic_)
args.pop() # Remove the last logic operator.
res = itertools.product(*args)
print " ".join(res.next())
# funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200
...
In this method i just cheated by replacing vars_ with ('A', 'B', 'C'). beside that i think it should work.
If you don't like my way of cheating by hard coding the the vars_ list and the funcs_ name you can get the name of your variable from the globals dictionary something like this:
def get_name(obj):
"""Get the name of an object (variable) from the globals dict.
Argument:
- obj : The variable that we want to get the name of.
Return:
- A string representing the name of the object if it was found else return None.
"""
for name, value in globals().items():
if value is obj:
return name
First of all, I would start by saying eval is a bad idea.There is always another way to do it.
Answer to your question:
Question 1:
Function name,
You can get using f.func_name.
Variable name,
Not so simple,but this should work,
import gc, sys
def find_names(obj):
frame = sys._getframe()
for frame in iter(lambda: frame.f_back, None):
frame.f_locals
result = []
for referrer in gc.get_referrers(obj):
if isinstance(referrer, dict):
for k, v in referrer.iteritems():
if v is obj:
result.append(k)
return result
a = 97
print find_names(a)[0]
It doesn't matter if it returns the wrong name since their value will be equal.
The operator and vol_limit is easily generated.
2nd Question.
No obvious solution out of mind. Iterate through all of them?
3rd Question.
Yes it is possible.
Check out Decorators.

Categories