Here is my function which is supposed to apply another function to every element within a given iterable.
def transform(iterable,f):
all=(i for i in iterable)
return (e.f() for e in all)
for i in transform('abCdeFg','upper'):
print(i,end='')
What it should do is capitalizing all the letters, but instead I get an error. What am I doing wrong? I'm using Python 3.3.
e.f is literally e.f. It has no relation to your f variable. To get an attribute by name, you use getattr:
def transform(iterable,f):
all=(i for i in iterable)
return (getattr(e, f)() for e in all)
for i in transform('abCdeFg','upper'):
print(i,end='')
Also, you may find the builtin map function useful:
def function(l):
return l.upper()
for i in map(function, 'abCdeFg'):
print(i, end='')
You made 2 errors:
To call a function f on argument e, you do f(e), not e.f()
To give a function as a parameter, gives its name, not a string with its name
So a corrected version would be:
def transform(iterable, f):
return (f(i) for i in iterable)
for i in transform('abCdeFg', str.upper):
print(i, end='')
The error is on this line:
return (e.f() for e in all)
The problem is that the str variable e doesn't have a method f(). You should instead:
def transform(iterable,f):
all=(i for i in iterable)
return (f(e) for e in all)
for i in transform('abCdeFg',str.upper):
print(i,end='')
If you want to do this just as you have written it, you need to do this:
def transform(s,f):
return getattr(type(s), f)(s)
for i in transform('abCdeFg','upper'):
print(i,end='')
Prints:
ABCDEFG
As others have said, this is more direct:
print('abCdeFg'.upper())
You should also not use Python built-ins as names. (i.e., Avoid calling a genex all because you will overwrite the built-in function all.)
With getattr you can also return a default of the type does not have the given method:
>>> def transform(s,f):
... return getattr(type(s), f, lambda s: 'no bueno')(s)
>>> transform(1,'upper')
'no bueno'
Or use try / except:
>>> def transform(s,f):
... try:
... return getattr(type(s), f)(s)
... except AttributeError as e:
... return e
...
>>> transform(1,'upper')
AttributeError("type object 'int' has no attribute 'upper'",)
You can also use join with map:
>>> ''.join(map(lambda c: str.upper(c), 'abCdeFg'))
'ABCDEFG'
Or join with a comprehension:
>>> ''.join(c.upper() for c in 'abCdeFg')
'ABCDEFG'
Related
I'm aware that I cann pass a function name as an argument to another function i.e
def fun_passed(a,b):
#do something
def main_fun(arg,fun_passed):
#do something
#call main_fun
first_fun =fun_passed
main_fun(1,first_fun)
Inside main_fun how can I make some checks.
For example, Based on the function i passed i want to set a variable
def main_fun(1,fun_passed):
if fun_passed==first_fun:
i=1
else:
i=10
I can't simply use == because i think that comparison doesn't make sense.
Thanks
You can compare functions for equality, but it's not going to check if the two functions do the exact same thing. Such a check is, in general, undecidable in the technical sense.
Instead, f == g simply returns true if both f and g return to the same underlying object (i.e., it's the same as f is g). This means that something as simple as (lambda x: x) == (lambda x: x) evaluates as False.
You should use the is keyword:
def fct_a(a,b):
#do something
pass
def fct_b(a,b):
#do something
pass
def main_fun(fct):
if fct is fct_a:
print("fct is 'fct_a'")
else:
print("fct is not 'fct_a'")
main_fun(fct_a) # >> fct is 'fun_passed'
main_fun(fct_b) # >> fct is not 'fun_passed'
For more about the differences between is and ==, see there
If you are dealing with functions, not a lambda, the function object has a variable __name__ which shows the exact name of the original function. Here a simple example:
>>> def x():
... return 0;
...
>>> type(x)
<type 'function'>
>>> x.__name__
'x'
>>> y=x
>>> y.__name__
'x'
So in your case, it will be something like this
if fun_passed.__name__=="first_fun":
I'm trying to implement a list of the same function called with different parameters, something like this:
def func1(num):
print("{}".format(num))
fl = [func1(1),func1(2),func1(3)]
for f in fl:
fl()
i got an error saying that 'list' object is not callable.
now, this works:
def func1():
print 1
def func2():
print 2
def func3():
print 3
fl = [func1,func2,func3]
for f in fl:
f()
what am i doing wrong?
From the code:
for f in f1:
f1()
Explanation:
As you defined f1 as of type list and you are using that variable as function call. But python expects the function name should be a string type. So got the error list() type is not callable
But when you changed code to :
for f in f1:
f()
here the f is of type string so python validated the type and so it didn't throw error
First, you're calling fl() which is trying to call the list as a function. that doesn't work.
In Python, putting () after a function evaluates the function and provides the return value. This means fl = [func1(1),func1(2),func1(3)] means "Create a list, and put the result of calling func1(1), func1(2), and func1(3)"
If you want to call the function later, then you can put the parameter into the list and walk through the list, or you can bind.
f = func1 # storing the function as a variable
fl = [1,2,3]
for v in fl:
f(v)
then you can do something like use lambda to bind:
f = func1 # storing the function as a variable
fl = [lambda :f(1), lambda :f(2), lambda :f(3)]
for v in fl:
v() # v is bound to the parameters, so you don't need to use a param
Use functools.partial:
from functools import partial
functions = [partial(func1, i) for i in range(1, 4)]
for fun in functions:
fun()
In your first example, I'm assuming that fl() is a typo, and you meant f() (because otherwise you'll be calling a list object, which isn't callable).
You know that func1(1) is calling the function and the result of this call is returned, so that in res = func1(1) the variable res will be the result of executing the function.. So, that list [func1(1), func1(2), ...] is a list of what this function returns. The function returns None, which isn't callable, that's why your code failed.
You want to call the function (not the result of calling the function!) with different arguments, and that's exactly what functools.partial is there for.
I have a file with a lot of lines like this
f(a, b)
f(abc, def)
f(a, f(u, i))
...
and I was asked to write a program in Python that would translate the strings into the following format:
a+b
abc+def
a+(u+i)
...
Rule: f(a, b) -> a+b
The approach I am following right now uses eval functions:
def f(a, b):
return "({0} + {1})".format(a,b)
eval("f(f('b','a'),'c')")
which returns
'((b + a) + c)'
However, as you can see, I need to put the letters as strings so that the eval function does not throw me a NameError when I run it.
Is there any way that will allow me to get the same behavior out of the eval function but without declaring the letters as strings?
eval is overkill here. this is just a simple string processing exercise:
replace the first 'f(' and the last ')' with ''
replace all remaining 'f(' with '('
replace all ', ' with '+'
and you're done.
this assumes that the only time the characters 'f(' appear next to each other is when it's supposed to represent a call to function f.
Yes, you can. The key is to use a mapping which returns the string as a key when it is missing.
>>> class Mdict(dict):
... def __missing__(self, k):
... return k
...
>>> eval('foo + bar', Mdict())
'foobar'
Of course, the general caveats about eval apply -- Please don't use it unless you trust the input completely.
You could use the shlex module to give yourself a nice token stack and then parse it as a sort of push down automaton.
>>> import shlex
>>> def parsef(tokens):
ftok = tokens.get_token() # there's no point to naming these tokens
oparentok = tokens.get_token() # unless you want to assert correct syntax
lefttok = tokens.get_token()
if 'f' == lefttok:
tokens.push_token(lefttok)
lefttok = "("+parsef(tokens)+")"
commatok = tokens.get_token()
righttok = tokens.get_token()
if 'f' == righttok:
tokens.push_token(righttok)
righttok = "("+parsef(tokens)+")"
cparentok = tokens.get_token()
return lefttok+"+"+righttok
>>> def parseline(line):
return parsef(shlex.shlex(line.strip()))
>>> parseline('f(a, b)')
'a+b'
>>> parseline('f(abc, def)')
'abc+def'
>>> parseline('f(a, f(u, i))')
'a+(u+i)'
Note that this assumes you are getting correct syntax.
Here is an example:
def g():
yield str('123')
yield int(123)
yield str('123')
o = g()
while True:
v = o.next()
if isinstance( v, str ):
print 'Many thanks to our generator...'
else:
# Or GOD! I don't know what to do with this type
raise TypeError( '%s:%d Unknown yield value type %s.' % \
(g.__filename__(), g.__lineno__(), type(v) )
)
How do I get the source file name and the exact yield line number, when my generator returns unknown type (int in this example)?
Your generator object "o" in this case has all the information you want. You can paste your example into a Python console, and inspect with dir both the function "g" and the generator "o".
The generator has the attributes "gi_code" and "gi_frame" which contain the information you want:
>>> o.gi_code.co_filename
'<stdin>'
# this is the line number inside the file:
>>> o.gi_code.co_firstlineno
1
# and this is the current line number inside the function:
>>> o.gi_frame.f_lineno
3
I don't think you can get the information you want. That's the sort of thing captured in exceptions, but there has been no exception here.
I have a generator and I would like to know if I can use it without having to worry about StopIteration , and I would like to use it without the for item in generator . I would like to use it with a while statement for example ( or other constructs ). How could I do that ?
built-in function
next(iterator[, default])
Retrieve the next item from the iterator by calling its __next__() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised.
In Python 2.5 and older:
raiseStopIteration = object()
def next(iterator, default=raiseStopIteration):
if not hasattr(iterator, 'next'):
raise TypeError("not an iterator")
try:
return iterator.next()
except StopIteration:
if default is raiseStopIteration:
raise
else:
return default
Another options is to read all generator values at once:
>>> alist = list(agenerator)
Example:
>>> def f():
... yield 'a'
...
>>> a = list(f())
>>> a[0]
'a'
>>> len(a)
1
Use this to wrap your generator:
class GeneratorWrap(object):
def __init__(self, generator):
self.generator = generator
def __iter__(self):
return self
def next(self):
for o in self.generator:
return o
raise StopIteration # If you don't care about the iterator protocol, remove this line and the __iter__ method.
Use it like this:
def example_generator():
for i in [1,2,3,4,5]:
yield i
gen = GeneratorWrap(example_generator())
print gen.next() # prints 1
print gen.next() # prints 2
Update: Please use the answer below because it is much better than this one.