Access an iterator in 'for' loop, before the loop - python

I am trying to access an iterator 'obj' before the 'for' loop like this
class MyClass:
CLASS_CONST1 = 'some_rand_const'
def __init__(self, type, age):
self.type = type
self.age = age
var1 = 5
age = 7
# # I tried to individually add the following lines here, none of them work
# obj = MyClass
# obj = MyClass()
condition = obj.type == MyClass.CLASS_CONST1
if var1 > 10:
condition = obj.type == MyClass.CLASS_CONST1 and obj.age == age
list_of_objects = [MyClass('rand1', 'rand2'), MyClass('rand1', 'rand2'), MyClass('rand1', 'rand2')]
for obj in list_of_objects:
if condition:
# do some stuff
pass
The issue is that it is accessed before it is defined (it gets defined in the for loop). And I dont want introduce the condition lines inside the 'for' loop because the lines would be executed in every iteration, and there is no need for that.
The idea is that all this goes into a function and 'var1' and 'age' are arguments of the function.

obj = MyClass just assigns the class object (not instance) to another variable. obj = MyClass() will throw an error because you haven't provided values for type and age which are required in __init__. Did you try obj = MyClass(var1, age) ? You did it later for list_of_objects.
Anyway, you've tried to create condition as a variable that is supposed to apply itself during the iteration. That's not how Python works. It's given a static value when it's evaluated the one time. To have it apply to all objects, have condition as a function which either take the object or the two variables type and var as parameters and then return the result of the check:
var1 = 5
age = 7
def condition(obj):
# will return the True/False result of the check below
return obj.type == MyClass.CLASS_CONST1 and obj.age == age
for obj in list_of_objects:
if condition(obj): # call the function with that object
# do some stuff
pass
From your code, it's unclear what you wanted in condition. Maybe it was this?
var1 = 5 # or put these inside `condition` so they are local
age = 7 # to condition and not globals.
# Or pass them in as parameters and modify `condition` to accept
# the additional params
def condition(obj):
result = obj.type == MyClass.CLASS_CONST1
if result and var1 > 10:
# don't re-check `obj.type == MyClass.CLASS_CONST1`
result = obj.age == age
return result

You declare condition as a simple boolean variable, while its value has to depend on the current values of obj. You could use a bunch of functions and assign condition to the relevant one, or as you conditions are simple, you could use lambdas:
condition = obj.type == MyClass.CLASS_CONST1
if var1 > 10:
condition = lambda obj: obj.type == MyClass.CLASS_CONST1 and obj.age == age
else:
condition = lambda obj: obj.type == MyClass.CLASS_CONST1
and then use it as a variable function:
for obj in list_of_objects:
if condition(obj):
# do some stuff
pass

Related

Make function parameters optional

I want the function to return a value when there is an error. I created this sample code, but it has two parameters. How can I make the function parameters optional? Because when I run the code below, my IDE returns an error that the parameters are required.
def test(error, parameter1):
if parameter1 == True:
print("true")
else:
print("false")
array = []
try:
array[0]
except Exception as e:
error = e
return error
value1 = 1
value2 = 2
if value1 == 1:
#I want this to print the error of the function above
print(test(error))
if value2 == 2:
# I want this to pass parameter to the function above
test(parameter1=True)
Optional paramteters are declared with =None, i.e.:
def test(error=None, parameter1=None):
...
in your function you'll have to check if the parameters are not none, i.e.:
if error is not None:
....
You could set the function parameters some default values so when no parameters are passed, the default values become the values of the variables:
def test(error = None, parameter1 = None):
if parameter1 == True:
print("true")
else:
print("false")
array = []
try:
array[0]
except Exception as e:
error = e
return error
Here None becomes a default value and if a parameter is passed, the value changes from None to the given value.

Is there a way to give a function access to the (external-scope) name of the variable being passed in? [duplicate]

This question already has answers here:
Getting the name of a variable as a string
(32 answers)
Closed 4 months ago.
Is it possible to get the original variable name of a variable passed to a function? E.g.
foobar = "foo"
def func(var):
print var.origname
So that:
func(foobar)
Returns:
>>foobar
EDIT:
All I was trying to do was make a function like:
def log(soup):
f = open(varname+'.html', 'w')
print >>f, soup.prettify()
f.close()
.. and have the function generate the filename from the name of the variable passed to it.
I suppose if it's not possible I'll just have to pass the variable and the variable's name as a string each time.
EDIT: To make it clear, I don't recommend using this AT ALL, it will break, it's a mess, it won't help you in any way, but it's doable for entertainment/education purposes.
You can hack around with the inspect module, I don't recommend that, but you can do it...
import inspect
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.getframeinfo(frame[0]).code_context[0].strip()
args = string[string.find('(') + 1:-1].split(',')
names = []
for i in args:
if i.find('=') != -1:
names.append(i.split('=')[1].strip())
else:
names.append(i)
print names
def main():
e = 1
c = 2
foo(e, 1000, b = c)
main()
Output:
['e', '1000', 'c']
To add to Michael Mrozek's answer, you can extract the exact parameters versus the full code by:
import re
import traceback
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
vars_name = re.compile(r'\((.*?)\).*$').search(code).groups()[0]
print vars_name
return
foobar = "foo"
func(foobar)
# PRINTS: foobar
Looks like Ivo beat me to inspect, but here's another implementation:
import inspect
def varName(var):
lcls = inspect.stack()[2][0].f_locals
for name in lcls:
if id(var) == id(lcls[name]):
return name
return None
def foo(x=None):
lcl='not me'
return varName(x)
def bar():
lcl = 'hi'
return foo(lcl)
bar()
# 'lcl'
Of course, it can be fooled:
def baz():
lcl = 'hi'
x='hi'
return foo(lcl)
baz()
# 'x'
Moral: don't do it.
Another way you can try if you know what the calling code will look like is to use traceback:
def func(var):
stack = traceback.extract_stack()
filename, lineno, function_name, code = stack[-2]
code will contain the line of code that was used to call func (in your example, it would be the string func(foobar)). You can parse that to pull out the argument
You can't. It's evaluated before being passed to the function. All you can do is pass it as a string.
#Ivo Wetzel's answer works in the case of function call are made in one line, like
e = 1 + 7
c = 3
foo(e, 100, b=c)
In case that function call is not in one line, like:
e = 1 + 7
c = 3
foo(e,
1000,
b = c)
below code works:
import inspect, ast
def foo(a, f, b):
frame = inspect.currentframe()
frame = inspect.getouterframes(frame)[1]
string = inspect.findsource(frame[0])[0]
nodes = ast.parse(''.join(string))
i_expr = -1
for (i, node) in enumerate(nodes.body):
if hasattr(node, 'value') and isinstance(node.value, ast.Call)
and hasattr(node.value.func, 'id') and node.value.func.id == 'foo' # Here goes name of the function:
i_expr = i
break
i_expr_next = min(i_expr + 1, len(nodes.body)-1)
lineno_start = nodes.body[i_expr].lineno
lineno_end = nodes.body[i_expr_next].lineno if i_expr_next != i_expr else len(string)
str_func_call = ''.join([i.strip() for i in string[lineno_start - 1: lineno_end]])
params = str_func_call[str_func_call.find('(') + 1:-1].split(',')
print(params)
You will get:
[u'e', u'1000', u'b = c']
But still, this might break.
You can use python-varname package
from varname import nameof
s = 'Hey!'
print (nameof(s))
Output:
s
Package below:
https://github.com/pwwang/python-varname
For posterity, here's some code I wrote for this task, in general I think there is a missing module in Python to give everyone nice and robust inspection of the caller environment. Similar to what rlang eval framework provides for R.
import re, inspect, ast
#Convoluted frame stack walk and source scrape to get what the calling statement to a function looked like.
#Specifically return the name of the variable passed as parameter found at position pos in the parameter list.
def _caller_param_name(pos):
#The parameter name to return
param = None
#Get the frame object for this function call
thisframe = inspect.currentframe()
try:
#Get the parent calling frames details
frames = inspect.getouterframes(thisframe)
#Function this function was just called from that we wish to find the calling parameter name for
function = frames[1][3]
#Get all the details of where the calling statement was
frame,filename,line_number,function_name,source,source_index = frames[2]
#Read in the source file in the parent calling frame upto where the call was made
with open(filename) as source_file:
head=[source_file.next() for x in xrange(line_number)]
source_file.close()
#Build all lines of the calling statement, this deals with when a function is called with parameters listed on each line
lines = []
#Compile a regex for matching the start of the function being called
regex = re.compile(r'\.?\s*%s\s*\(' % (function))
#Work backwards from the parent calling frame line number until we see the start of the calling statement (usually the same line!!!)
for line in reversed(head):
lines.append(line.strip())
if re.search(regex, line):
break
#Put the lines we have groked back into sourcefile order rather than reverse order
lines.reverse()
#Join all the lines that were part of the calling statement
call = "".join(lines)
#Grab the parameter list from the calling statement for the function we were called from
match = re.search('\.?\s*%s\s*\((.*)\)' % (function), call)
paramlist = match.group(1)
#If the function was called with no parameters raise an exception
if paramlist == "":
raise LookupError("Function called with no parameters.")
#Use the Python abstract syntax tree parser to create a parsed form of the function parameter list 'Name' nodes are variable names
parameter = ast.parse(paramlist).body[0].value
#If there were multiple parameters get the positional requested
if type(parameter).__name__ == 'Tuple':
#If we asked for a parameter outside of what was passed complain
if pos >= len(parameter.elts):
raise LookupError("The function call did not have a parameter at postion %s" % pos)
parameter = parameter.elts[pos]
#If there was only a single parameter and another was requested raise an exception
elif pos != 0:
raise LookupError("There was only a single calling parameter found. Parameter indices start at 0.")
#If the parameter was the name of a variable we can use it otherwise pass back None
if type(parameter).__name__ == 'Name':
param = parameter.id
finally:
#Remove the frame reference to prevent cyclic references screwing the garbage collector
del thisframe
#Return the parameter name we found
return param
If you want a Key Value Pair relationship, maybe using a Dictionary would be better?
...or if you're trying to create some auto-documentation from your code, perhaps something like Doxygen (http://www.doxygen.nl/) could do the job for you?
I wondered how IceCream solves this problem. So I looked into the source code and came up with the following (slightly simplified) solution. It might not be 100% bullet-proof (e.g. I dropped get_text_with_indentation and I assume exactly one function argument), but it works well for different test cases. It does not need to parse source code itself, so it should be more robust and simpler than previous solutions.
#!/usr/bin/env python3
import inspect
from executing import Source
def func(var):
callFrame = inspect.currentframe().f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
expression = source.asttokens().get_text(callNode.args[0])
print(expression, '=', var)
i = 1
f = 2.0
dct = {'key': 'value'}
obj = type('', (), {'value': 42})
func(i)
func(f)
func(s)
func(dct['key'])
func(obj.value)
Output:
i = 1
f = 2.0
s = string
dct['key'] = value
obj.value = 42
Update: If you want to move the "magic" into a separate function, you simply have to go one frame further back with an additional f_back.
def get_name_of_argument():
callFrame = inspect.currentframe().f_back.f_back
callNode = Source.executing(callFrame).node
source = Source.for_frame(callFrame)
return source.asttokens().get_text(callNode.args[0])
def func(var):
print(get_name_of_argument(), '=', var)
If you want to get the caller params as in #Matt Oates answer answer without using the source file (ie from Jupyter Notebook), this code (combined from #Aeon answer) will do the trick (at least in some simple cases):
def get_caller_params():
# get the frame object for this function call
thisframe = inspect.currentframe()
# get the parent calling frames details
frames = inspect.getouterframes(thisframe)
# frame 0 is the frame of this function
# frame 1 is the frame of the caller function (the one we want to inspect)
# frame 2 is the frame of the code that calls the caller
caller_function_name = frames[1][3]
code_that_calls_caller = inspect.findsource(frames[2][0])[0]
# parse code to get nodes of abstract syntact tree of the call
nodes = ast.parse(''.join(code_that_calls_caller))
# find the node that calls the function
i_expr = -1
for (i, node) in enumerate(nodes.body):
if _node_is_our_function_call(node, caller_function_name):
i_expr = i
break
# line with the call start
idx_start = nodes.body[i_expr].lineno - 1
# line with the end of the call
if i_expr < len(nodes.body) - 1:
# next expression marks the end of the call
idx_end = nodes.body[i_expr + 1].lineno - 1
else:
# end of the source marks the end of the call
idx_end = len(code_that_calls_caller)
call_lines = code_that_calls_caller[idx_start:idx_end]
str_func_call = ''.join([line.strip() for line in call_lines])
str_call_params = str_func_call[str_func_call.find('(') + 1:-1]
params = [p.strip() for p in str_call_params.split(',')]
return params
def _node_is_our_function_call(node, our_function_name):
node_is_call = hasattr(node, 'value') and isinstance(node.value, ast.Call)
if not node_is_call:
return False
function_name_correct = hasattr(node.value.func, 'id') and node.value.func.id == our_function_name
return function_name_correct
You can then run it as this:
def test(*par_values):
par_names = get_caller_params()
for name, val in zip(par_names, par_values):
print(name, val)
a = 1
b = 2
string = 'text'
test(a, b,
string
)
to get the desired output:
a 1
b 2
string text
Since you can have multiple variables with the same content, instead of passing the variable (content), it might be safer (and will be simpler) to pass it's name in a string and get the variable content from the locals dictionary in the callers stack frame. :
def displayvar(name):
import sys
return name+" = "+repr(sys._getframe(1).f_locals[name])
If it just so happens that the variable is a callable (function), it will have a __name__ property.
E.g. a wrapper to log the execution time of a function:
def time_it(func, *args, **kwargs):
start = perf_counter()
result = func(*args, **kwargs)
duration = perf_counter() - start
print(f'{func.__name__} ran in {duration * 1000}ms')
return result

how to fix the _getitem__ method

def pnamedtuple(type_name, field_names, mutable=False):
pass
class type_name:
def __init__(self, x, y):
self.x = x
self.y = y
self._fields = ['x','y']
self._mutable = False
def get_x(self):
return self.x
def get_y(self):
return self.y
def __getitem__(self,i):
if i > 1 or i <0:
raise IndexError
if i == 0 or i == 'x':
return self.get_x():
if i == 1 or i == 'y':
return self.get_y():
the getitem method to overload the [] (indexing operator) for this class: an index of 0 returns the value of the first field name in the field_names list; an index of 1 returns the value of the second field name in the field_names list, etc. Also, the index can be a string with the named field. So, for p = Point(1,2) writing p.get_x(), or p[0]), or p['x'] returns a result of 1. Raise an IndexError with an appropriate message if the index is out of bounds int or a string that does not name a field.
I am not sure how to fix the getitme function. below is the bsc.txt
c-->t1 = Triple1(1,2,3)
c-->t2 = Triple2(1,2,3)
c-->t3 = Triple3(1,2,3)
# Test __getitem__ functions
e-->t1[0]-->1
e-->t1[1]-->2
e-->t1[2]-->3
e-->t1['a']-->1
e-->t1['b']-->2
e-->t1['c']-->3
^-->t1[4]-->IndexError
^-->t1['d']-->IndexError
^-->t1[3.2]-->IndexError
can someone tell how to fix my _getitem _ function to get the output in bsc.txt? many thanks.
You've spelled __getitem__ incorrectly. Magic methods require two __ underscores before and after them.
So you haven't overloaded the original __getitem__ method, you've simply created a new method named _getitem_.
Python 3 does not allow strings and integers to be compared with > or <; it's best to stick with == if you don't yet know the type of i. You could use isinstance, but here you can easily convert the only two valid integer values to strings (or vice versa), then work only on strings.
def __getitem__(self, i):
if i == 0:
i = "x"
elif i == 1:
i = "y"
if i == "x":
return self.get_x()
elif i == "y":
return self.get_y()
else:
raise IndexError("Invalid key: {}".format(i))
your function is interesting, but there are some issues with it:
In python 3 you can't compare string with numbers, so you first should check with == against know values and or types. For example
def __getitem__(self,i):
if i in {0,"x"}:
return self.x
elif i in {1,"y"}:
return self.y
else:
raise IndexError(repr(i))
But defined like that (in your code or in the example above) for an instance t1 this t1[X] for all string X others than "x" or "y" will always fail as you don't adjust it for any other value. And that is because
pnamedtuple looks like you want for it to be a factory like collections.namedtuple, but it fail to be general enough because you don't use any the arguments of your function at all. And no, type_name is not used either, whatever value it have is throw away when you make the class declaration.
how to fix it?
You need other ways to store the value of the fields and its respective name, for example a dictionary lets call it self._data
To remember how you called yours field, use the argument of your function, for instance self._fields = field_names
To accept a unknown number of arguments use * like __init__(self, *values) then verify that you have the same numbers of values and fields and build your data structure of point 1 (the dictionary)
Once that those are ready then __getitem__ become something like:
def __getitem__(self, key):
if key in self._data:
return self._data[key]
elif isintance(key,int) and 0 <= key < len(self._fields):
return self._data[ self._fields[key] ]
else:
raise IndexError( repr(key) )
or you can simple inherit from a appropriate namedtuple and the only thing you need to do is overwrite its __getitem__ like
def __getitem__(self,key):
if key in self._fields:
return getattr(self,key)
return super().__getitem__(key)

How to loop through keys in a dict to find the corresponding value?

When a user enters a name (e.g. "Jim") as an argument for an instance of my "Test" class, the def find function is called and for-loops through all the names in the dict matching "Jim". If def find finds the key word "Jim" in the dict, then it should print out the corresponding value. But when I run the code it just says "None". What do I need to change so that invoking def find results in the print statement 'worked'??
class Test(object):
def __init__(self, x=0): # error in (def find)?
self.x = x
c = None # error while looping in the for loop?
users = {
'John': 1,
'Jim': 2,
'Bob': 3
}
def find(self, x): # The user is supposed to type in the name "x"
for self.c in self.users: # it goes through the dictionary
if x == self.users[self.c]: # If x is equal to key it prints worked
print('worked')
else:
pass
beta = Test()
print(beta.find('Jim'))
#nk001,
I think this is a little more like what you are trying for:
class Test(object):
def __init__(self, x=0):
self.x = x # <-- indent the __init__ statements
users = { # <-- users = {
'John': 1, # KEY: VALUE,
'Jim': 2, # KEY: VALUE,
'Bob': 3 # KEY: VALUE,
} # }
def find(self, x): # <-- The user passes the "x" argument
for i in self.users: # <-- Now it goes through the dictionary
if x == i: # <-- If ARGV('x') == KEY
return 'worked' # <-- Then RETURN 'worked'
else:
pass
beta = Test()
print(beta.find("Jim"), beta.users["Jim"])
There's a couple different ways to get the 'worked' msg and the corresponding Value printed, this is just an example to demonstrate accessing the dict[KEY] to get the VALUE.
Also, I'm just assuming you meant an if/else block, and not a for/else? Indentation is critical w/Python. Also, your original script was returning None because there was no explicit return in your for loop - hence, when the function is called in the printing statement print(beta.find('Jim')) when the function finishes it returns nothing ("None"). Hope that helps!
I write a worked code:
class Test(object):
users = {
'John': 1,
'Jim': 2,
'Bob': 3
}
def __init__(self, x=0): # So I don't get an error in (def find)
self.x = x
def find(self, x): # The user is suppose to type in the name "x"
for name in Test.users.keys(): # it goes through the dictionary
if x == name: # If x is equal to key it prints worked
print('worked', self.users[name])
else:
pass
beta = Test()
beta.find('Jim')
You don not need the self.c.
The users is a class variable, you need to visit it by Test.users.
Your names is stored as the keys of the dict. So you need to get them by Test.users.keys()
The statement print(beta.find('Jim')) will print the return value of the find. But you don't return a value manually, you will get a None in your output.

Language: Python(vers3.3); If statements inside while and if statements

My program is unable to go into the last of my if/else statement.
My code:
def main_prog():
while True:
data_dict = {'123': 'viksun', '111': ['tord'], '333': ['test']} # Data storage.
print (data_dict) # Track-print
prompt = input('Enter command >>> ').split(" ")
if prompt[0] == 'lookup':
B_value = name_lookup_return_boolean(data_dict, prompt[1])
if B_value == True:
print (prompt[1],"'s number is:", name_lookup(data_dict, prompt[1]))
else:
print("The name:" ,prompt[1], "do not exist, please try a different name")
Data struction of the dictionary: data_dict
data_dict = {'123': ['name1'], '111': ['tord'], '333': ['test']} # Data storage.
Function descriptions:
- name_lookup_returns_boolean(data_dict, name) returns a boolean value: True if the name is located in the dictionary and False if the name does not exist.
- name_lookup returns the key that corresponds to the specific name.
The functions have been tested separately from the whole program, so they are working. If you need them, I can also post them.
I can't get the program to run the last else statement in the program. This is going to run if the name does not exist in the dictionary: data_dict.
Here is the implementation of the two functions:
def name_lookup(data_dict, name):
for key, value in data_dict.items():
if name in value:
return key
def name_lookup_return_boolean(data_dict, name):
for value in data_dict.items():
if name in value:
return True
else:
return False
This is the variation I have tried to use_but with no sucess:
def name_lookup_version_02(data_dict, name):
for value in data_dict.values():
if name in value:
return True
else:
return False
In this line:
if B_value == True:
I'm guessing that B_value holds a true value, but not True. Try this instead:
if B_value:
The problem is in your name_lookup_return_boolean function. You are returning True for both conditions. Also you should be enumerating both key and value otherwise value will be assigned a tuple.
It should look like this:
def name_lookup_return_boolean(data_dict, name):
for key,value in data_dict.items():
if name in value:
return True
UPDATE: After testing, I realised previous answer was wrong - was only matching the first value

Categories