The trick is to use globals[]:
globals[]['use_variable_as_function_name'][]
will be equivalent to
use_variable_as_function_name[]
found at: George Sakkis //bytes.com/topic/python/answers/792283-calling-variable-function-name
The following is a useful application of the above questioning I needed right now [that's why I came here]: apply special functions to URLs depending on their nature:
l = ['condition1', 'condition2', 'condition3']
I used to write
if 'condition1.' in href:
return do_something_condition1[]
if 'condition2.' in href:
return do_something_condition2[]
if 'condition3.' in href:
return do_something_condition3[]
and so on - my list has 19 members by now and keeps growing.
While investigating the subject and developing, the function code had been quite naturally part of the main function making it soon horrible to read, so relocating the working code into functions was a great relief already.
This clumsy code above can be substituted by:
for e in l: # this is my condition list
if e + '.' in href: # this is the mechanism to choose the right function
return globals[]['do_something_' + e][]
This way the main code stays simple and legible no matter how long the list of conditions may grow.
Those functions corresponding to the condition labels have to be declared conventionally, of course, depending on the nature of the type of the URL in question:
def do_something_condition1[href]:
# special code 1
print['========1=======' + href]
def do_something_condition2[href]:
# special code 2
print['========2=======' + href]
def do_something_condition3[href]:
# special code 3
print['========3=======' + href]
Test:
>>> href = '//google.com'
>>> for e in l:
... globals[]['do_something_' + e][href]
...
========1=======//google.com
========2=======//google.com
========3=======//google.com
Or, to model it closer to the above scenario:
success = '________processed successfully___________ '
def do_something_google[href]:
# special code 1
print['========we do google-specific stuff=======']
return success + href
def do_something_bing[href]:
# special code 2
print['========we do bing-specific stuff=======']
return success + href
def do_something_wikipedia[href]:
# special code 3
print['========we do wikipedia-specific stuff=======']
return success + href
Test:
l = ['google', 'bing', 'wikipedia']
href = '//google.com'
def test[href]:
for e in l:
if e + '.' in href:
return globals[]['do_something_' + e][href]
>>> test[href]
========we do google-specific stuff=======
'________processed successfully___________ //google.com'
Result:
Further elaboration on the problem now just amounts to augment the condition list one by one and write the corresponding functions depending on the argument. The above mechanism will pick the right one thereafter.
In Python there are three things that can be considered being name of the function:
The original name of the code block
It’s stored in the f.__code__.co_name
[where f
is the function object]. If you use def orig_name
to create function, orig_name
is that name. For lambas it's .
This attribute is readonly and can’t be changed. So the only way to create function with the custom name in runtime I’m aware of is exec
:
[There is also more low-level way to do this that.]
The immutability of co_name
actually makes sense: with that you can be sure that the name you see in the debugger [or just stack trace] is exactly the same you see in the source code [along with the filename and line number].
The
__name__
attribute of the function object
It’s also aliased to func_name
.
You can modify it [orig_name.__name__ = 'updated name'
] and you surely do on a daily basis: @functools.wraps
copies the __name__
of the decorated function to the new one.
__name__
is used by tools like pydoc
, that's why you need @functools.wraps
: so you don't see the technical details of every decorator in your documentation. Look at the example:
Here is the pydoc
output:
FUNCTIONS
decorator1[f] decorator2[f] test1 = decorated[*args, **kwargs] test2[*args, **kwargs]
With wraps
there is no sign of decorated
in the documentation.
Name of the reference
One more thing that can be called function name [though it hardly is] is the name of a variable or an attribute where reference to that function is stored.
If you create function with def name
, the name
attribute will be added to the current scope. lambda
, on the other hand, just returns a new value [which also can
be assigned to attribute or variable: name = lambda: None
`].
Obviously you can create more than one reference to the same function and all that references can have different names.
The only way all that three things are connected to each other is thedef foo
statement that creates function object with both __name__
and __code__.co_name
equal to foo
and assign it to the foo
attribute of the current scope. But they are not bound in any way and can be different
from each other:
Output:
File "my.py", line 13, in
name_in_module[]
File "my.py", line 7, in orig_name
traceback.print_stack[]
Pydoc:
FUNCTIONS
make_function[] name_in_module = updated name[]
Docstring here
This article was originally posted as an answer to my own Stack Overflow question. I thank other people for comments and answers, they helped me to organize my thoughts and knowledge.