Access a function variable outside the function without using "global" [duplicate] - python

This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed 4 months ago.
I am trying to access a local function variable outside the function in Python.
I can make code like this work with global variables:
bye = ''
def hi():
global bye
bye = 5
sigh = 10
hi()
print(bye)
Next, I tried this code, hoping to access bye outside hi() without using global bye:
def hi():
bye = 5
sigh = 10
return
hi()
x = hi()
print(x.bye)
This gives AttributeError: 'NoneType' object has no attribute 'bye'.
Next, I tried:
def hi():
bye = 5
sigh = 10
return bye
hi()
x = hi()
print(x.bye)
This didn't improve matters; I get AttributeError: 'int' object has no attribute 'bye'.
Is there a way to access a local function variable (bye) outside its function (hi()) without using globals and also without printing out the sigh variable? How can I do it?

You could do something along these lines (which worked in both Python v2.7.17 and v3.8.1 when I tested it/them):
def hi():
# other code...
hi.bye = 42 # Create function attribute.
sigh = 10
hi()
print(hi.bye) # -> 42
Functions are objects in Python and can have arbitrary attributes assigned to them.
If you're going to be doing this kind of thing often, you could implement something more generic by creating a function decorator that adds a this argument to each call to the decorated function.
This additional argument will give functions a way to reference themselves without needing to explicitly embed (hardcode) their name into the rest of the definition and is similar to the instance argument that class methods automatically receive as their first argument which is usually named self — I picked something different to avoid confusion, but like the self argument, it can be named whatever you wish.
Here's an example of that approach:
def add_this_arg(func):
def wrapped(*args, **kwargs):
return func(wrapped, *args, **kwargs)
return wrapped
#add_this_arg
def hi(this, that):
# other code...
this.bye = 2 * that # Create function attribute.
sigh = 10
hi(21)
print(hi.bye) # -> 42
Note
This doesn't work for class methods. Just use the instance argument, named self by convention, that's already passed to methods instead of the method's name. You can reference class-level attributes through type(self). See Function's attributes when in a class.

The problem is you were calling print(x.bye) after you set x as a string. When you run x = hi() it runs hi() and sets the value of x to 5 (the value of bye; it does NOT set the value of x as a reference to the bye variable itself). EX: bye = 5; x = bye; bye = 4; print(x) prints 5, not 4.
Also, you don't have to run hi() twice, just run x = hi(), not hi(); x=hi() (the way you had it it was running hi(), not doing anything with the resulting value of 5, and then rerunning the same hi() and saving the value of 5 to the x variable.
So full code should be
def hi():
bye = 5
sigh = 10
return bye
x = hi()
print(x)
If you wanted to return multiple variables, one option would be to use a list, or dictionary, depending on what you need. For example:
def hi():
return { 'bye': 5, 'sigh': 10 }
x = hi()
print x['bye']

def hi():
bye = 5
return bye
print hi()

You could do something along this lines:
def static_example():
if not hasattr(static_example, "static_var"):
static_example.static_var = 0
static_example.static_var += 1
return static_example.static_var
print static_example()
print static_example()
print static_example()

To be able to access a local function's variable, one might add the name of the function and a dot before the name of the local variable (and then, of course, use this construction for calling the variable both in the function's body and outside of it). This solution works in Python 3.7.4.
For example:
def func(): # define a function
# here y is a local variable, which I want to access; func.y
# defines a method for my example function which will allow me to
# access function's local variable y
func.y = 4
x = func.y + 8
return x
func() # now I'm calling the function
a = func.y # I put its local variable into my new variable
print(a) # and print my new variable

If you want to avoid global, one possible approach is to define a class. Each class instance has its own attributes; there is also a class attribute space where instances can share an attribute between them.
Object-oriented programming can be challenging to get into if you are new to Python, but this might actually be a good time to start playing with it.
class Thing:
shared = "foo"
def __init__(self):
"""
This gets called when you create a new Thing()
"""
self.bar = "baz" # default value for new instances
def get_bar(self):
return self.bar
def set_bar(self, value):
self.bar = value
Now, let's create two instances.
first = Thing()
second = Thing()
The get_bar and set_bar methods are not strictly necessary in simple examples like this one. You can also do
second.bar = "ick"
print(second.bar)
# "ick"
print(first.bar)
# "baz"
(though for more complex scenarios, you probably want to require users to call the setter and getter methods; there are ways to force this - see e.g. What's the pythonic way to use getters and setters?)
If you change a class attribute via one instance, it will not be changed in the other instances, either.
second.shared = "poo"
print(first.shared)
# "foo"
But if you change it in the class itself, it will be changed in all the instances which have not separately overridden the shared value.
Thing.shared = "zoom"
print(first.shared)
# "zoom"
print(second.shared)
# "poo", still
To recap, you create a new Thing instance by calling Thing(); this will run the __init__ method before returning the new instance. Inside the class, the instance is the first argument to every (non-static, non-class) method, and conventionally called self (though you could get away with calling it shirley if you wanted to, as far as the Python interpreter is concerned).
There's a lot more to classes; the main selling point is probably that you can create subclasses which inherit from their parent class but can override some behaviors (common examples often involve real-world concepts like animals or vehicles, but a class can just be anything where you want to create a type and encapsulate its behavior, and perhaps override some methods in derived types).

Related

how to access list, declared inside a function, outside the function? [duplicate]

This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed 4 months ago.
I am trying to access a local function variable outside the function in Python.
I can make code like this work with global variables:
bye = ''
def hi():
global bye
bye = 5
sigh = 10
hi()
print(bye)
Next, I tried this code, hoping to access bye outside hi() without using global bye:
def hi():
bye = 5
sigh = 10
return
hi()
x = hi()
print(x.bye)
This gives AttributeError: 'NoneType' object has no attribute 'bye'.
Next, I tried:
def hi():
bye = 5
sigh = 10
return bye
hi()
x = hi()
print(x.bye)
This didn't improve matters; I get AttributeError: 'int' object has no attribute 'bye'.
Is there a way to access a local function variable (bye) outside its function (hi()) without using globals and also without printing out the sigh variable? How can I do it?
You could do something along these lines (which worked in both Python v2.7.17 and v3.8.1 when I tested it/them):
def hi():
# other code...
hi.bye = 42 # Create function attribute.
sigh = 10
hi()
print(hi.bye) # -> 42
Functions are objects in Python and can have arbitrary attributes assigned to them.
If you're going to be doing this kind of thing often, you could implement something more generic by creating a function decorator that adds a this argument to each call to the decorated function.
This additional argument will give functions a way to reference themselves without needing to explicitly embed (hardcode) their name into the rest of the definition and is similar to the instance argument that class methods automatically receive as their first argument which is usually named self — I picked something different to avoid confusion, but like the self argument, it can be named whatever you wish.
Here's an example of that approach:
def add_this_arg(func):
def wrapped(*args, **kwargs):
return func(wrapped, *args, **kwargs)
return wrapped
#add_this_arg
def hi(this, that):
# other code...
this.bye = 2 * that # Create function attribute.
sigh = 10
hi(21)
print(hi.bye) # -> 42
Note
This doesn't work for class methods. Just use the instance argument, named self by convention, that's already passed to methods instead of the method's name. You can reference class-level attributes through type(self). See Function's attributes when in a class.
The problem is you were calling print(x.bye) after you set x as a string. When you run x = hi() it runs hi() and sets the value of x to 5 (the value of bye; it does NOT set the value of x as a reference to the bye variable itself). EX: bye = 5; x = bye; bye = 4; print(x) prints 5, not 4.
Also, you don't have to run hi() twice, just run x = hi(), not hi(); x=hi() (the way you had it it was running hi(), not doing anything with the resulting value of 5, and then rerunning the same hi() and saving the value of 5 to the x variable.
So full code should be
def hi():
bye = 5
sigh = 10
return bye
x = hi()
print(x)
If you wanted to return multiple variables, one option would be to use a list, or dictionary, depending on what you need. For example:
def hi():
return { 'bye': 5, 'sigh': 10 }
x = hi()
print x['bye']
def hi():
bye = 5
return bye
print hi()
You could do something along this lines:
def static_example():
if not hasattr(static_example, "static_var"):
static_example.static_var = 0
static_example.static_var += 1
return static_example.static_var
print static_example()
print static_example()
print static_example()
To be able to access a local function's variable, one might add the name of the function and a dot before the name of the local variable (and then, of course, use this construction for calling the variable both in the function's body and outside of it). This solution works in Python 3.7.4.
For example:
def func(): # define a function
# here y is a local variable, which I want to access; func.y
# defines a method for my example function which will allow me to
# access function's local variable y
func.y = 4
x = func.y + 8
return x
func() # now I'm calling the function
a = func.y # I put its local variable into my new variable
print(a) # and print my new variable
If you want to avoid global, one possible approach is to define a class. Each class instance has its own attributes; there is also a class attribute space where instances can share an attribute between them.
Object-oriented programming can be challenging to get into if you are new to Python, but this might actually be a good time to start playing with it.
class Thing:
shared = "foo"
def __init__(self):
"""
This gets called when you create a new Thing()
"""
self.bar = "baz" # default value for new instances
def get_bar(self):
return self.bar
def set_bar(self, value):
self.bar = value
Now, let's create two instances.
first = Thing()
second = Thing()
The get_bar and set_bar methods are not strictly necessary in simple examples like this one. You can also do
second.bar = "ick"
print(second.bar)
# "ick"
print(first.bar)
# "baz"
(though for more complex scenarios, you probably want to require users to call the setter and getter methods; there are ways to force this - see e.g. What's the pythonic way to use getters and setters?)
If you change a class attribute via one instance, it will not be changed in the other instances, either.
second.shared = "poo"
print(first.shared)
# "foo"
But if you change it in the class itself, it will be changed in all the instances which have not separately overridden the shared value.
Thing.shared = "zoom"
print(first.shared)
# "zoom"
print(second.shared)
# "poo", still
To recap, you create a new Thing instance by calling Thing(); this will run the __init__ method before returning the new instance. Inside the class, the instance is the first argument to every (non-static, non-class) method, and conventionally called self (though you could get away with calling it shirley if you wanted to, as far as the Python interpreter is concerned).
There's a lot more to classes; the main selling point is probably that you can create subclasses which inherit from their parent class but can override some behaviors (common examples often involve real-world concepts like animals or vehicles, but a class can just be anything where you want to create a type and encapsulate its behavior, and perhaps override some methods in derived types).

Is there a simple and efficient way to use a global variable into a method of a class with Python

I have a code with a class using many variables.i can't Access one of those variables to use it inside of a method of my code written in Python.
I tried to declare the variable as global to make the value being accessible at any point of the code but it is not ok. I am expecting to use the variable in a specific method
In Python, "variables" are really "names" and you can access them whenever they're in scope!
However, if you intend to modify a name that's outside of the current scope, you need to tell the interpreter so (otherwise it'll assume that you mean to give the name to something else within your scope)
a = None
def foo():
# you can access a, but not set it here
print(a)
a = None
def foo():
a = 1 # a is only set within foo
print(a) # refers to a in outer scope
usage of global
a = None
def foo():
global a
# now you can modify a
a = 1
creates an error
a = None
def foo():
print(a)
global a # SyntaxError: name 'a' is used prior to global declaration
There is no easy way. Use (global a) when you are changing the variable in the function.
For example when you are changing the variable in a function.
a = 0
def asi():
global a
a = 1
Output : 1
For example when you are not changing the variable but want to use it in a function.
a = 0
def asi():
print(a)
Output : 0
your question is not clear to me:
you didn't share any code for interpretation
in OOP ( us said class and methods ) how could you not be able to access variable (global variable or class variable: both may not same but its for sake of argument)
by 'in my code written in python' what do you mean actually? with in same class? method? module? venv?
Note:- every question may not have answer in coding it may be literature... ( that whats logic is.....)

__closure__ attribute of function object always be 'None' when defining func inside exec()

EDIT2:
A minimal demonstration is:
code = """\
a=1
def f1():
print(a)
print(f1.__closure__)
f1()
"""
def foo():
exec(code)
foo()
Which gives:
None
Traceback (most recent call last):
File "D:/workfiles/test_eval_rec.py", line 221, in <module>
foo()
File "D:/workfiles//test_eval_rec.py", line 219, in foo
exec(code)
File "<string>", line 5, in <module>
File "<string>", line 3, in f1
NameError: name 'a' is not defined
It can be seen that the __closure__ attribute of function defined inside code str passed to exec() is None, making calling the function fails.
Why does this happen and how can I define a function successfully?
I find several questions that may be related.
Closure lost during callback defined in exec()
Using exec() with recursive functions
Why exec() works differently when invoked inside of function and how to avoid it
Why are closures broken within exec?
NameError: name 'self' is not defined IN EXEC/EVAL
These questions are all related to "defining a function insdie exec()". I think the fourth question here is closest to the essence of these problems. The common cause of these problems is that when defining a function in exec(), the __closure__ attribute of the function object can not be set correctly and will always be None. However, many existing answers to this question didn't realize this point.
Why these questions are caused by wrong __closure__:
When defining a function, __closure__ attribute is set to a dict that contains all local symbols (at the place where the keyword def is used) that is used inside the newly defined funtion. When calling a function, local symbol tables will be retrived from the __closure__ attribute. Since the __closure__ is set to None, the local symbol tables can not be retrived as expected, making the function call fail.
These answers work by making None a correct __closure__ attribute:
Existing solutions to the questions listed above solve these problems by getting the function definition rid of the usage of local symbol, i.e, they make the local symbols used(variable, function definition) global by passing globals() as locals of exec or by using keyword global explicitly in the code string.
Why existing solution unsatisfying:
These solutions I think is just an escape of the core problem of setting __closure__ correctly when define a functioni inside exec(). And as symbols used in the function definition is made global, these solutions will produce redundant global symbol which I don't want.
Original Questions:
(You May ignore this session, I have figured something out, and what I currently want to ask is described as the session EDIT2. The original question can be viewed as a sepecial case of the question described in session EDIT2)
original title of this question is: Wrapping class function to new function with exec() raise NameError that ‘self’ is not defined
I want to wrap an existing member function to a new class function. However, exec() function failed with a NameError that ‘self’ is not defined.
I did some experiment with the following codes. I called globals() and locals() in the execed string, it seems that the locals() is different in the function definition scope when exec() is executed. "self" is in the locals() when in exec(), however, in the function definition scope inside the exec(), "self" is not in the locals().
class test_wrapper_function():
def __init__(self):
# first wrapper
def temp_func():
print("locals() inside the function definition without exec:")
print(locals())
return self.func()
print("locals() outside the function definition without exec:")
print(locals())
self.wrappered_func1 = temp_func
# third wrapper using eval
define_function_str = '''def temp_func():
print("locals() inside the function definition:")
print(locals())
print("globals() inside the function definition:")
print(globals())
return self.func()
print("locals() outside the function definition:")
print(locals())
print("globals() outside the function definition:")
print(globals())
self.wrappered_func2 = temp_func'''
exec(define_function_str)
# call locals() here, it will contains temp_func
def func(self):
print("hi!")
t = test_wrapper_function()
print("**********************************************")
t.wrappered_func1()
t.wrappered_func2()
I have read this link. In the exec(), memeber function, attribute of "self" can be accessed without problem, while in the function difinition in the exec(), "self" is not available any more. Why does this happen?
Why I want to do this:
I am building a PyQt program. I want to create several similar slots(). These slots can be generated by calling one member function with different arguments. I decided to generate these slots using exec() function of python. I also searched with the keyword "nested name scope in python exec", I found this question may be related, but there is no useful answer.
To be more specific. I want to define a family of slots like func_X (X can be 'a', 'b', 'c'...), each do something like self.do_something_on(X). Here, do_something is a member function of my QWidget. So I use a for loop to create these slots function. I used codes like this:
class MyWidget():
def __init__(self):
self.create_slots_family()
def do_something(self, character):
# in fact, this function is much more complex. Do some simplification.
print(character)
def create_slots_i(self, character):
# want to define a function like this:
# if character is 'C', define self.func_C such that self.func_C() works like self.do_something(C)
create_slot_command_str = "self.func_" + character + " = lambda:self.do_something('" + character + "')"
print(create_slot_command_str)
exec(create_slot_command_str)
def create_slots_family(self):
for c in ["A", "B", "C", "D"]:
self.create_slots_i(c)
my_widget = MyWidget()
my_widget.func_A()
Note that, as far as I know, the Qt slots should not accept any parameter, so I have to wrap self.do_something(character) to be a series function self.func_A, self.func_C and so on for all the possible characters.
So the above is what I want to do orignially.
EDIT1:
(You May ignore this session, I have figured something out, and what I currently want to ask is described as the session EDIT2. This simplified version of original question can also be viewed as a sepecial case of the question described in session EDIT2)
As #Mad Physicist suggested. I provide a simplified version here, deleting some codes used for experiments.
class test_wrapper_function():
def __init__(self):
define_function_str = '''\
def temp_func():
return self.func()
self.wrappered_func2 = temp_func'''
exec(define_function_str)
def func(self):
print("hi!")
t = test_wrapper_function()
t.wrappered_func2()
I expected this to print a "hi". However, I got the following exception:
Traceback (most recent call last):
File "D:/workfiles/test_eval_class4.py", line 12, in <module>
t.wrappered_func2()
File "<string>", line 2, in temp_func
NameError: name 'self' is not defined
Using Exec
You've already covered most of the problems and workarounds with exec, but I feel that there is still value in adding a summary.
The key issue is that exec only knows about globals and locals, but not about free variables and the non-local namespace. That is why the docs say
If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.
There is no way to make it run as though it were in a method body. However, as you've already noted, you can make exec create a closure and use that instead of the internal namespace by adding a method body to your snippet. However, there are still a couple of subtle restrictions there.
Your example of what you are trying to do showcases the issues perfectly, so I will use a modified version of that. The goal is to make a method that binds to self and has a variable argument in the exec string.
class Test:
def create_slots_i(self, c):
create_slot_command_str = f"self.func_{c} = lambda: self.do_something('{c}')"
exec(create_slot_command_str)
def do_something(self, c):
print(f'I did {c}!')
There are different ways of getting exec to "see" variables: literals, globals, and internal closures.
Literals. This works robustly, but only for simple types that can be easily instantiated from a string. The usage of c above is a perfect example. This will not help you with a complex object like self:
>>> t = Test()
>>> t.create_slots_i('a')
>>> t.func_a()
...
NameError: name 'self' is not defined
This happens exactly because exec has no concept of free variables. Since self is passed to it via the default locals(), it does not bind the reference to a closure.
globals. You can pass in a name self to exec via globals. There are a couple of ways of doing this, each with its own issues. Remember that globals are accessed by a function through its __globals__ (look at the table under "Callable types") attribute. Normally __globals__ refers to the __dict__ of the module in which a function is defined. In exec, this is the case by default as well, since that's what globals() returns.
Add to globals: You can create a global variable named self, which will make your problem go away, sort of:
>>> self = t
>>> t.func_a()
I did a!
But of course this is a house of cards that falls apart as soon as you delete, self, modify it, or try to run this on multiple instances:
>>> del self
>>> t.func_a()
...
NameError: name 'self' is not defined
Copy globals. A much more versatile solution, on the surface of it, is to copy globals() when you run exec in create_slots_i:
def create_slots_i(self, c):
create_slot_command_str = f"self.func_{c} = lambda: self.do_something('{c}')"
g = globals().copy()
g['self'] = self
exec(create_slot_command_str, g)
This appears to work normally, and for a very limited set of cases, it actually does:
>>> t = Test()
>>> t.create_slots_i('a')
>>> t.func_a()
I did a!
But now, your function's __globals__ attribute is no longer bound to the module you created it in. If it uses any other global values, especially ones that might change, you will not be able to see the changes. For limited functionality, this is OK, but in the general case, it can be a severe handicap.
Internal Closures. This is the solution you already hit upon, where you create a closure within the exec string to let it know that you have a free variable by artificial means. For example:
class Test:
def create_slots_i(self, c):
create_slot_command_str = f"""def make_func(self):
def func_{c}():
self.do_something('{c}')
return func_{c}
self.func_{c} = make_func(self)"""
g = globals().copy()
g['self'] = self
exec(create_slot_command_str, g)
def do_something(self, c):
print(f'I did {c}!')
This approach works completely:
>>> t = Test()
>>> t.create_slots_i('a')
>>> t.func_a()
I did a!
The only real drawbacks here are security, which is always a problem with exec, and the sheer awkwardness of this monstrosity.
A Better Way
Since you are already creating closures, there is really no need to use exec at all. In fact, the only thing you are really doing is creating methods so that self.func_... will bind the method for you, since you need a function with the signature of your slot and access to self. You can write a simple method that will generate functions that you can assign to your slots directly. The advantage of doing it this way is that (a) you avoid calling exec entirely, and (b) you don't need to have a bunch of similarly named auto-generated methods polluting your class namespace. The slot generator would look something like this:
def create_slots_i(self, c):
def slot_func():
self.do_something(c) # This is a real closure now
slot_func.__name__ = f'func_{c}'
return slot_func
Since you will not be referring to these function objects anywhere except your slots, __name__ is the only way to get the "name" under which they were stored. That is the same thing that def does for you under the hood.
You can now assign slots directly:
some_widget.some_signal.connect(self.create_slots_i('a'))
Note
I originally had a more complex approach in mind for you, since I thought you cared about generating bound methods, instead of just setting __name__. In case you have a sufficiently complex scenario where it still applies, here is my original blurb:
A quick recap of the descriptor protocol: when you bind a function with the dot operator, e.g., t.func_a, python looks at the class for descriptors with that name. If your class has a data descriptor (like property, but not functions), then that descriptor will shadow anything you may have placed in the instance __dict__. However, if you have a non-data descriptor (one a __get__ method but without a __set__ method, like a function object), then it will only be bound if an instance attribute does not shadow it. Once this decision has been made, actually invoking the descriptor protocol involves calling type(t).func_a.__get__(t). That's how a bound method knows about self.
Now you can return a bound method from within your generator:
def create_slots_i(self, c):
def slot_func(self):
self.do_something(c) # This is a closure on `c`, but not on `self` until you bind it
slot_func.__name__ = f'func_{c}'
return slot_func.__get__(self)
Why this phenomena happen:
Actually the answer of the question 4 listed above can answer this question.
When call exec() on one code string, the code string is first compiled. I suppose that during compiling, the provided globals and locals is not considered. The symbol in the exec()ed code str is compiled to be in the globals. So the function defined in the code str will be considered using global variables, and thus __closure__ is set to None.
Refer to this answer for more information about what the func exec does.
How to deal with this phenomena:
Imitating the solutions provided in the previous questions, for the minimal demostration the question, it can also be modified this way to work:
a=1 # moving out of the variable 'code'
code = """\
def f1():
print(a)
print(f1.__closure__)
f1()
"""
def foo():
exec(code)
foo()
Although the __closure__ is still None, the exception can be avoided because now only the global symbol is needed and __closure__ should also be None if correctly set. You can read the part The reason why the solutions work in the question body for more information.
This was originally added in Revision 4 of the question.
TL;DR
To set correct __closure__ attribute of function defined in the code string passed to exec() function. Just wrap the total code string with a function definition.
I provide an example here to demonstrate all possible situations. Suppose you want to define a function named foo inside a code string used by exec(). The foo use function, variables that defined inside and outside the code string:
def f1():
outside_local_variable = "this is local variable defined outside code str"
def outside_local_function():
print("this is function defined outside code str")
code = """\
local_variable = "this is local variable defined inside code str"
def local_function():
print("this is function defined inside code str")
def foo():
print(local_variable)
local_function()
print(outside_local_variable)
outside_local_function()
foo()
"""
exec(code)
f1()
It can be wrapper like this:
def f1():
outside_local_variable = "this is local variable defined outside code str"
def outside_local_function():
print("this is function defined outside code str")
code = """\
def closure_helper_func(outside_local_variable, outside_local_function):
local_variable = "this is local variable defined inside code str"
def local_function():
print("this is function defined inside code str")
def foo():
print(local_variable)
local_function()
print(outside_local_variable)
outside_local_function()
foo()
closure_helper_func(outside_local_variable, outside_local_function)
"""
exec(code)
f1()
Detailed explanation:
Why the __closure__ attribute is not corretly set:
please refer to The community wiki answer.
How to set the __closure__ attribute to what's expected:
Just wrap the whole code str with a helper function definition and call the helper function once, then during compiling, the variables are considered to be local, and will be stored in the __closure__ attribute.
For the minimal demonstration in the question, it can be modified to following:
code = """\
def closure_helper_func():
a=1
def f1():
print(a)
print(f1.__closure__)
f1()
closure_helper_func()
"""
def foo():
exec(code)
foo()
This output as expected
(<cell at 0x0000019CE6239A98: int object at 0x00007FFF42BFA1A0>,)
1
The example above provide a way to add symbols that defined in the code str to the __closure__ For example, in the minimal demo, a=1 is a defined inside the code str. But what if one want to add the local symbols defined outside the code str? For example, in the code snippet in EDIT1 session, the self symbol needs to be added to the __closure__, and the symbol is provided in the locals() when exec() is called. Just add the name of these symbols to the arguments of helper function and you can handle this situation.
The following shows how to fix the problem in EDIT1 session.
class test_wrapper_function():
def __init__(self):
define_function_str = '''\
def closure_helper_func(self):
def temp_func():
return self.func()
self.wrappered_func2 = temp_func
closure_helper_func(self)
'''
exec(define_function_str)
def func(self):
print("hi!")
t = test_wrapper_function()
t.wrappered_func2()
The following shows how to fix the codes in the session "Why I want to do this"
class MyWidget():
def __init__(self):
self.create_slots_family()
def do_something(self, character):
# in fact, this function is much more complex. Do some simplification.
print(character)
def create_slots_i(self, character):
# want to define a function like this:
# if character is 'C', define self.func_C such that self.func_C() works like self.do_something(C)
# create_slot_command_str = "self.func_" + character + " = lambda:self.do_something('" + character + "')"
create_slot_command_str = """
def closure_helper_func(self):
self.func_""" + character + " = lambda:self.do_something('" + character + """')
closure_helper_func(self)
"""
# print(create_slot_command_str)
exec(create_slot_command_str)
def create_slots_family(self):
for c in ["A", "B", "C", "D"]:
self.create_slots_i(c)
my_widget = MyWidget()
my_widget.func_A()
This solution seems to be too tricky. However, I can not find a more elegant way to declare that some variables should be local symbol during compiling.

python 3: Class function member to modify a class data member [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python: How do I pass a variable by reference?
I have the following case:
class A:
def __init__(self):
self.test = ''
self.func(self.test)
print(self.test)
def func(self,var):
var = 'foo'
I want func to modify self.var and I'd like to be able to pass a self. to this method.
A bit like:
class A()
{
public:
char test[256];
A() { func(test);}
private:
void func(char * var) { var = "foo"; }
};
I haven't written C++ in a while but that's sort of what I'm going for.
Unfortunately, I don't know C++ very well, but I'm guessing you want something like this:
class A:
def __init__(self):
self.test = ''
self.func("test")
print(self.test)
def func(self,var):
setattr(self,var,'foo')
We have to do it this way because we can change (mutate) self.test inside a function (if it's mutable), but we can't change which object it references (which is what you're attempting to do with assignment). consider:
def func1(x):
x.append('foo')
def func2(x):
x = 'foo'
a = []
func1(a)
print a #['foo'] #mutated a in func1
func2(a)
print a #['foo'] #didn't change a in func2, only created a new local variable named `x` and assigned it to the string 'foo'
The only way around this is to pass some sort of proxy-like object which you can change. In this case, we pass the instance as the proxy object (self) and the attribute's name (var) so we know what to change on self. Given those two pieces of information, we can make the desired changes -- Of course, at this point, you're probably best off getting rid of func all together and just using setattr directly.
It's probably also worth asking why you actually want to do this. In general, you can just do:
self.test = "foo"
why would you want to write:
self.func(self.test)
instead? I can understand if you're trying to do that since you're used to having private methods and attributes. But this is python. "We're all consenting adults here." Just rely on the conventions (prefix a variable with _ if you want to warn users against modifying it and prefix with __ to invoke name mangling to avoid collisions in superclasses).

creating a class that behaves like any variable but has callback on change/read

I would like to create a class that behaves as a python variable but calls some callback function when the "variable" is changed/read.
In other words, I'd like to be able to use the class as follows:
x=myClass(change_callback, read_callback)
defines x as an instance of myclass. The constructor (INIT) takes 2 functions as paramater: a function to be called each time x is changed, and a function to be called each time x is "read"
The following statement:
x=1
would "save" 1 and trigger a call to change_callback(1) which could do anything.
The following statement:
a=x
would retrieve the stored value and call read_callback() which would possibly change the stored value and do other things.
I would like this to work with any type, e.g. to be able to write things like:
x=[1 2 3] which would trigger change_callback([1 2 3])
x.append(4) would trigger change_callback([1 2 3 4]) (and possibly a call to read_callback() before)
x={'a':1} would trigger change_callback({'a':1})
print(x) would trigger a call to read_callback()...and return the last stored value for printing, of course.
The idea is that any access to the variable could be logged, or generate other calculation... seemlessly.
I get the feeling this should be possible, but I don't really see what my object should inherit from...
If I have to restrict me to one type,e.g. a list, is there any way to redefine all assignment operators (including methods like append()...) in "one go", keeping the original behaviour (the base class method) and adding the callback...
Or are there more appropriate ways (modules...) to achieve the same goals...?
Thanks in advance,
Objects don't know when they get assigned to a variable. Writing x = a adds a dict entry (or locals entry) that points to a. The a object doesn't get notified (though, in CPython, its reference count gets incremented).
The part that does get notified is the container where the object is assigned. In the case of a global assignment, the module dictionary gets updated. In the case of instance variable updates like a.b = x, there is a dotted lookup and store to the instance dictionary.
You can make those containers invoke arbitrary code on the lookup or store. Python's property provides this capability to new-style classes. The exec and eval operations let you specify a custom dict that can provide this capability to regular assignments. In both cases, you are in complete control of what happens upon assignment.
Summary: Lookup and store behaviors are controllable through the target of the assignment rather than the object being assigned.
Example:
from collections import namedtuple
CallbackVar = namedtuple('CallbackVar', ['change_callback', 'read_callback'])
class CallbackDict(dict):
'Variant of dict that does callbacks for instances of CallbackVar'
def __getitem__(self, key):
value = dict.__getitem__(self, key)
if isinstance(value, CallbackVar):
return value.read_callback(key)
def __setitem__(self, key, value):
try:
realvalue = dict.__getitem__(self, key)
if isinstance(realvalue, CallbackVar):
return realvalue.change_callback(key, value)
except KeyError:
pass
return dict.__setitem__(self, key, value)
stmts = '''
x = CallbackVar(setter, getter) # Attach getter() and setter() to "x"
x = 1 # Invoke the setter()
x # Invoke the getter()
'''
def getter(key):
print 'Getting', key
return 42
def setter(key, value):
print 'Setting', key, 'to', value
exec stmts in globals(), CallbackDict()
You might like to look at descriptors: http://docs.python.org/howto/descriptor.html
This will only work for object properties, though, which is probably enough for you.
You can't do this with the = operator - since by definition, it's designed to overwrite the left-hand side, whatever the current contents of that are don't get any notification that such is happening. (And the assignment operator is not override-able in Python.)
What you could do, however, is use .set() and .get() methods on your class. It wouldn't get you quite the same "magic", but honestly, that's probably a good thing - it makes it clearer what's going on.
Variable is a reference to an python object. It's wrong to say that "a class behave like a variable".
when you do
x = [1, 2, 3]
the variable x will be a reference(redirected) to the new list object [1, 2, 3], not your old object was "changed". It just lost a reference.
To do what you may want. Try to call change_callback in the __init__ method of your class.
To have the read_callback called. Do something like this
class A(object):
def __init__(self):
self._x = 3
#property
def x():
read_callback() # called!
return self._x
....
a = A()
print a.x
If this is what you what.

Categories