Accessing outer function parameters in nested functions giving weird behavior? [duplicate] - python

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Python nested functions variable scoping [duplicate]
(10 answers)
Closed 3 years ago.
I have the following piece of code, it works and gives the correct output:
def msg_before(msg=None):
def actual_decorator(func):
def wrapper(*args, **kwargs):
print(msg if msg is not None else "was none") # original
result = func(*args, **kwargs)
return result
return wrapper
return actual_decorator
#msg_before("this comes before")
def test_func():
print("test")
if __name__ == "__main__":
test_func()
output:
this comes before
test
But when I change the wrapper function like this:
def msg_before(msg=None):
def actual_decorator(func):
def wrapper(*args, **kwargs):
msg = msg if msg is not None else "was none" # changed
print(msg) # changed
result = func(*args, **kwargs)
return result
return wrapper
return actual_decorator
I get the following error:
Traceback (most recent call last):
File "C:/Users/ruohola/Desktop/test.py", line 33, in <module>
test_func()
File "C:/Users/ruohola/Desktop/test.py", line 19, in wrapper
msg = msg if msg is not None else "was none" # changed
UnboundLocalError: local variable 'msg' referenced before assignment
To me both of the codes seem identical, and I just don't understand where the error comes from and why it doesn't occur in the first code?

Related

Python. How to call a function from a dictionary? [duplicate]

This question already has an answer here:
What does <function at ...> mean [duplicate]
(1 answer)
Closed 4 years ago.
I want to have a dictionary where a certain key calls a value... that value is a function and I would want that function to execute.
Below is my attempt to do it but all I get is the value of where it is stored in memory.
class Testing:
def __init__(self):
self.method = {'Method1': self.another, 'Method2': self.there}
def launch(self, input):
print(self.method[input])
#staticmethod
def another():
print('This print statement should pop out.')
#staticmethod
def there():
print('This should not appear.')
new = Testing()
new.launch('Method1')
The result that I get from that is:
<function Testing.another at 0x01519540>
Is there a way to do this?
You are missing the actual function call: (notice the added () at the end)
def launch(self, input):
print(self.method[input]())

Handling `NameError` without raising `SyntaxError` when referencing global variables in python3

Handling a NameError is raising a SyntaxError:
So I'm making a decorator function which associates a generator-function to a dictionary, with the intention of making the send() function less cumbersome to use.
The default behavior is to use a dictionary named global_registry_dict defined in (surprise!) the global-namespace.
If global_registry_dict does not exist and an alternative dictionary is not explicitly passed in, NameError is raised by python.
When I try to handle the error by creating the variable in the global namespace, I get a SyntaxError telling me that the variable is referenced before assignment. That makes sense...but...like...that's what I'm trying to fix!
Any help would be greatly appreciated!
Source for the Decorator:
def make_stateless_actor(func):
def wrapper(registry_dict=None, *args, **kwargs):
try:
if registry_dict is None:
internal_registry_dict = global_registry_dict
elif isinstance(registry_dict, dict) or issubclass(registry_dict, dict):
internal_registry_dict = registry_dict
else:
internal_registry_dict = global_registry_dict
except NameError: # <<== CATCHES IF `global_registry_dict` IS REF'D BUT NOT DEFINED.
global global_registry_dict # <<== SYNTAX ERROR HAPPENS HERE
global_registry_dict = {}
internal_registry_dict = global_registry_dict
gen = func(*args, **kwargs)
next(gen)
internal_registry_dict[func.__name__] = gen
return wrapper
Exception Returned:
File "C:/yall/dont/need/to/know/this/generator_pipeline_utils.py", line 74
global global_registry_dict
^
SyntaxError: name 'global_registry_dict' is used prior to global declaration
Source of the Entire Module:
# global_registry_dict = {}
def make_stateless_actor(func):
"""
DESCRIPTION:
===========
This DECORATOR FUNCTION simplifies & centralizes the use of `send()` on generators.
It does this by ASSOCIATING the decorated function to a dictionary:
- by default, a global variable named `global_registry_dict` will be used.
- by default, `global_registry_dict = {}` will be created in the GLOBAL-NAMESPACE if it does not exist.
IMPORTANT NOTE:
==============
Use the `registry_dict` kwarg to manually assign your generator-func to an alternate dictionary:
`foo_decorated_func(registry_dict=foo_alternative_dictionary)`
REQUIREMENTS:
============
You can apply `#make_stateless_actor` to any GENERATOR FUNCTION which:
- INCLUDES A `yield` expression to the RIGHT of an `=` sign.
- INCLUDES `**kwargs` as a function argument.
"""
def wrapper(registry_dict=None, *args, **kwargs):
try:
if registry_dict is None: # <<-- necessary to prevent the next elif from throwing an exception.
internal_registry_dict = global_registry_dict
elif isinstance(registry_dict, dict) or issubclass(registry_dict, dict):
internal_registry_dict = registry_dict
else:
internal_registry_dict = global_registry_dict
except NameError:
print('WARNING: global_registry_dict IS NOT DEFINED AND YOU DID NOT EXPLICITLY PASS registry_dict TO THE DECORATED FUNCTION.\n\
`global_registry_dict = {}` has been instantiated in the global-namespace.\n')
global global_registry_dict
global_registry_dict = {}
internal_registry_dict = global_registry_dict
gen = func(*args, **kwargs)
next(gen)
internal_registry_dict[func.__name__] = gen
return wrapper
#make_stateless_actor
def printer(*args, **kwargs):
while True:
item = yield
print("Got: ", item)
def send(name, msg, registry_dict=None):
if registry_dict is None:
global_registry_dict[name].send(msg)
else:
registry_dict[name].send(msg)
if __name__ == "__main__":
printer(registry_dict=None) #<<==BEGIN THE CRAZY.
# send('printer', 'HEY WHAT IS UP?')
# send('printer', 666)
# send('printer', [1, 'a', [], True])
You use the name global_registry_dict in the else clause a couple lines up. If it's to be global, you must declare it so before using it, just as the error message says. Generally, names are declared global at the beginning of the function.

Check if a function was callled [duplicate]

This question already has answers here:
Find Out If a Function has been Called
(6 answers)
Closed 5 years ago.
I want to check if a function was called inside a Python script.
My current solution is the following:
def _print_error(msg):
print(msg)
print("Default error message.")
_print_error.was_called = True
_print_error.was_called = False
I can check if _print_error was called by checking _print_error.was_called.
Another possible way to do this is to set a class and define a static variable, that is changed whenever the class is instanced.The __init__ can print the messages and change the was_called variable.
Is there a better way of achieving the same results?
Use a decorator to set the flag:
from functools import wraps
def flag_when_called(func):
func.was_called = False
#wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
func.was_called = True
return result
return wrapper
Note that I only set the flag when the call successfully completes (e.g. if an exception is raised the flag is not set). You can swap the order (and directly return func(*args, **kwargs) if you want to set the flag even if an exception was raised.
Just apply that to the function:
#flag_when_called
def _print_error(msg):
print(msg)
print("Default error message.")
It's okay to do what you do. If you do it often you can write a decorator for this:
import functools
def trackcalls(func):
#functools.wraps(func)
def wrapper(*args, **kwargs):
wrapper.was_called = True
return func(*args, **kwargs)
wrapper.was_called = False
return wrapper
#trackcalls
def _print_error(msg):
print(msg)
print("Default error message.")
_print_error()
# and then check it:
if _print_error.was_called:
print("Alarm!")

call 2 functions in a function [duplicate]

This question already has answers here:
Assign multiple functions to a single variable?
(3 answers)
Closed 6 years ago.
The problem is as follows below:
Write a function compose that takes two functions as argument, we call them Fa and Fb, and returns a function, Fres, that means that outdata from the other function is indata to the first, ex: Fres(x) = Fa(Fb(x)).
run example:
>>> def multiply_five(n):
... return n * 5
...
>>> def add_ten(x):
... return x + 10
...
>>> composition = compose(multiply_five, add_ten)
>>> composition(3)
65
>>> another_composition = compose(add_ten, multiply_five)
>>> another_composition(3)
25
So as I understand this if I send in 3 the function compose will take 3+10 = 13
after that send that result into the multiply function it will do: 13*5 witch is 65.
this is the code I've written so far:
def multiply_five(n):
return n*5
def add_ten(x):
return x+10
def compose(func1, func2):
def comp(arg):
return func2(arg)
return func1(comp(arg))
I get compile error, and I've tried some different approaches:
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
composition = compose(multiply_five, add_ten)
File "C:\Users\Jim\Desktop\tdp002\5h.py", line 10, in compose
return func1(comp(arg))
NameError: name 'arg' is not defined
You don't want to call either func1 or func2 yet; you just want to return a function that will call them both.
def compose(func1, func2):
def _(*args, **kw):
return func1(func2(*args, **kw))
return _
You could also just use a lambda expression to create the composed function.
def compose(func1, func2):
return lambda *args, **kw: func1(func2(*args, **kw))
Try this:
def compose(func1, func2):
def comp(arg):
return func1(func2(arg))
return comp

Returning a function in Python [duplicate]

This question already has answers here:
Interesting 'takes exactly 1 argument (2 given)' Python error
(6 answers)
Closed 6 years ago.
I get the following error "TypeError: make_pretty() takes exactly 1 argument (2 given)" for the below code snippet. I'm learning python and any help would be helpful...
class test:
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
pretty1 = test()
pretty = pretty1.make_pretty(pretty1.ordinary)
print(pretty())
I also tried it with decorators, but still face the same error..
class SmartDivide:
def smart_divide(self,func):
def inner(a,b):
print("I am going to divide",a,"and",b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a,b)
return inner
#smart_divide
def divide(self,a,b):
return a/b
dvd = SmartDivide()
print(dvd.divide(2,5))
dvd.divide(2,0)
You may need the self keyword
class test:
def make_pretty(self, func):
def inner():
print("I got decorated")
func()
return inner
def ordinary(self):
print("I am ordinary")
pretty1 = test()
pretty = pretty1.make_pretty(pretty1.ordinary)
print(pretty())

Categories

Resources