Changing Global Variables in Python - python

I want to modify a global variable from a function in Python 2.7
x = 0
def func():
global x
x = 2
If I load this code in the interpreter, and then run func(), x remains 0. How do I modify the value of x from within a function?
EDIT: Here's a screenshot of the interpreter and source code. I'm not sure why it works for others and not for me.
http://img18.imageshack.us/img18/9567/screenshotfrom201304222.png

This is a very interesting situation. When I ran your code from an interpreter with from mytest import * I encountered the same issue:
>>> from mytest import *
>>> x
0
>>> func()
>>> x
0
However, when I just did import mytest and ran it from there:
>>> import mytest
>>> mytest.x
0
>>> mytest.func()
>>> mytest.x
2
It turned out fine! The reason, I believe, comes from a line in http://docs.python.org/2/reference/simple_stmts.html#the-global-statement:
Names listed in a global statement must not be defined as formal
parameters or in a for loop control target, class definition, function
definition, or import statement.
Looks like because it is a parameter in your import statement (by importing all), global is having trouble with it. Do you need to import *, or can you simply import the module whole?

Related

python monkey patch a new class and import it

I want to monkey patch a new class and then import that class but I get an error ModuleNotFoundError. At the same time I can use the new patched function. I believe that I miss how to add it in the "init" file.
See the following simplified example:
import numpy
class wrong_functions():
def wrong_add(num1, num2):
return num1 + num2 + 1
numpy.random.wrong_functions = wrong_functions
numpy.random.wrong_functions.wrong_add(1,1) # works
from numpy.random.wrong_functions import wrong_add # does not work
from numpy.random.wrong_functions import * # does not work
What do you think? Is that possible?
This is because of the import system.
Reading through the doc, you can find this paragraph:
[...] the statement from spam.ham import eggs, sausage as saus results in
_temp = __import__('spam.ham', globals(), locals(), ['eggs', 'sausage'], 0)
eggs = _temp.eggs
saus = _temp.sausage
The problem is: what does __import__() does?
The import statement combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope. [...]
A direct call to __import__() performs only the module search and, if found, the module creation operation.
So, when you re-import the module, your customization will be lost.
To ensure it stays on, you can import numpy as np and then, when using np - after you assing this new class - you can always access wrong_add.
>>> import numpy as np
>>> np.random.wrong_functions = wrong_functions
>>> np.random.wrong_function.wrong_add(1, 1)
3
EDIT: If you need/want to just call wrong_add instead of full package path to function, you can always assign it to a variable.
>>> wrong_add = np.random.wrong_function.wrong_add
>>> wrong_add(2, 2)
5

Imported function can print value of variable without the variable being imported but i can't, why does this happen?

I have recently tried experimenting with importing files, this time I imported a function and I dont understand how the function can print the variable x's value but if I try to print x an error happens, can you explain why?
First.py
x=5
def a():
print(x)
Second.py
from First import a
a()
print(x)
>>>5
>>>NameError: name 'x' is not defined
I thought that either:
a() and print(x) both wouldn't work because I didn't import x as well
or that they would both work because variables inside a() would also be imported, I didn't expect this though
In First.py, you have defined two things: x and a.
When you run from First import a in Second.py, you explicitly stated that you want to import a and nothing else in the file.
Therefore, x cannot be accessed in Second.py, as it was not imported.
As mentioned in inspectorG4dget's comment, you could use from First import x as well, or you could do from First import * to import all defined values in First.py. There are other options as well, depending on your use case.

Override globals in function imported from another module

Let's say I have two modules:
a.py
value = 3
def x()
return value
b.py
from a import x
value = 4
My goal is to use the functionality of a.x in b, but change the value returned by the function. Specifically, value will be looked up with a as the source of global names even when I run b.x(). I am basically trying to create a copy of the function object in b.x that is identical to a.x but uses b to get its globals. Is there a reasonably straightforward way to do that?
Here is an example:
import a, b
print(a.x(), b.x())
The result is currently 3 3, but I want it to be 3 4.
I have come up with two convoluted methods that work, but I am not happy with either one:
Re-define x in module b using copy-and paste. The real function is much more complex than shown, so this doesn't sit right with me.
Define a parameter that can be passed in to x and just use the module's value:
def x(value):
return value
This adds a burden on the user that I want to avoid, and does not really solve the problem.
Is there a way to modify where the function gets its globals somehow?
I've come up with a solution through a mixture of guess-and-check and research. You can do pretty much exactly what I proposed in the question: copy a function object and replace its __globals__ attribute.
I am using Python 3, so here is a modified version of the answer to the question linked above, with an added option to override the globals:
import copy
import types
import functools
def copy_func(f, globals=None, module=None):
"""Based on https://stackoverflow.com/a/13503277/2988730 (#unutbu)"""
if globals is None:
globals = f.__globals__
g = types.FunctionType(f.__code__, globals, name=f.__name__,
argdefs=f.__defaults__, closure=f.__closure__)
g = functools.update_wrapper(g, f)
if module is not None:
g.__module__ = module
g.__kwdefaults__ = copy.copy(f.__kwdefaults__)
return g
b.py
from a import x
value = 4
x = copy_func(x, globals(), __name__)
The __globals__ attribute is read-only, which is why it must be passed to the constructor of FunctionType. The __globals__ reference of an existing function object can not be changed.
Postscript
I've used this enough times now that it's implemented in a utility library I wrote and maintain called haggis. See haggis.objects.copy_func.
So I found a way to (sort of) do this, although I don't think it entirely solves your problems. Using inspect, you can access the global variables of the file calling your function. So if you set up your files like so:
a.py
import inspect
value = 3
def a():
return inspect.stack()[1][0].f_globals['value']
b.py
from a import a
value = 5
print(a())
The output is 5, instead of 3. However, if you imported both of these into a third file, it would look for the globals of the third file. Just wanted to share this snippet however.
I had the same problem. But then I remembered eval was a thing.
Here's a much shorter version(if you don't need arguments):
b.py:
from a import x as xx
# Define globals for the function here
glob = {'value': 4}
def x():
return eval(xx.__code__, glob)
Hopefully after 2 years it'll still be helpful

How Can I Suppress Warnings in Python Module CppHeaderParser

How Do I Change the Value of a Variable or Function in a Foreign Module?
I have a C++ header file that I need to parse. I'm using CppHeaderParser. Sadly, the header generates a lot of warnings that I'd like to suppress. The header in question is maintained by someone else, so I can't just fix it and be done.
CppHeaderParser doesn't include a configurable way to suppress warnings, but it is controlled by a variable and a function in the module
# Controls warning_print
print_warnings = 1
...
def warning_print(arg):
if print_warnings: print(("[%4d] %s"%(inspect.currentframe().f_back.f_lineno, arg)))
In my script, I tried changing the value of print_warnings:
import CppHeaderParser
CppHeaderParser.print_warnings = 0
cpp_info = CppHeaderParser.CppHeader(my_h_file)
But this had no effect.
How do I set a variable in a different module such that a class defined in that module will see it?
In my case, I might also like to redefine warning_print to examine the warnings and skip only the specific warnings I wish to ignore. I encountered the same problem as setting print_warnings. The assignment "worked" but had no effect, as if the code in CppHeaderParser wasn't looking at the values I set.
Note: I have worked around the problem by making a temp copy of the header file, correcting the problems, but I consider this a fragile solution.
Update: I was able to completely and unintelligently suppress all the warnings with:
CppHeaderParser.CppHeaderParser.print_warnings = 0
I've looked the source. The problem with your method is that in CppHeaderParser file there are import with *:
from .CppHeaderParser import *
So you need to change the way you import CppHeaderParser class:
from CppHeaderParser import CppHeaderParser
It should work.
Finally, just try this:
from CppHeaderParser import CppHeaderParser
CppHeaderParser.print_warnings = 0
cpp_info = CppHeaderParser.CppHeader(my_h_file)
The reason of such a behaviour is that from statement creates the copy of variable from imported module but not an alias. I will try to explain it on a simple example. Let's suppose we have some module named import_test with the following contents:
foo = "Init value"
def f():
print(foo)
Then execute the following code:
>> from import_test import *
>> f()
Init value
>> foo = "Updated value"
>> f()
Init value
The reason is that you change the copy of variable foo so the actual value of import_test.foo variable is not changed.
But we have a different behaviour when we import the module itself:
>> import import_test
>> import_test.f()
Init value
>> import_test.foo = "Updated value"
>> import_test.f()
Updated value
So in the case of CppHeaderParser package when you make import CppHeaderParser the code inside CppHeaderParser.__init__ is executed. And python interpreter creates a copy of warnings_print variable inside a CppHeaderParser. But to change the behaviour of a print_warning function you have to change the value of CppHeaderParser.CppHeaderParser.warnings_print.
Try monkey patching:
import CppHeaderParser
def my_silent_warning_print(arg):
pass
CppHeaderParser.warning_print = my_silent_warning_print

Python global variables don't seem to work across modules

Code
I'd like to use a global variable in other modules with having changes to its value "propagated" to the other modules.
a.py:
x="fail"
def changeX():
global x
x="ok"
b.py:
from a import x, changeX
changeX()
print x
If I run b.py, I'd want it to print "ok", but it really prints "fail".
Questions
Why is that?
How can I make it print "ok" instead?
(Running python-2.7)
In short: you can't make it print "ok" without modifying the code.
from a import x, changeX is equivalent to:
import a
x = a.x
changeX = a.changeX
In other words, from a import x doesn't create an x that indirects to a.x, it creates a new global variable x in the b module with the current value of a.x. From that it follows that later changes to a.x do not affect b.x.
To make your code work as intended, simply change the code in b.py to import a:
import a
a.changeX()
print a.x
You will have less cluttered imports, easier to read code (because it's clear what identifier comes from where without looking at the list of imports), less problems with circular imports (because not all identifiers are needed at once), and a better chance for tools like reload to work.
You can also add another import statement after changeX. This would turn the code from b.py into
from a import x, changeX
changeX()
from a import x
print x
This illustrates that by calling changeX, only x in module a is changed. Importing it again, binds the updated value again to the identifier x.
Also you can use mutable container, for example list:
a.py
x = ['fail']
def changeX():
x[0] = 'ok'
b.py
from a import changeX, x
changeX()
print x[0]

Categories