Lifetime of variable after if block - python

There is this code:
>>> if True:
... a = 4
...
>>> print a
4
Why variable a is still alive after if block? Shouldn't it be destroyed when block if ends?

Python variables have scope inside a function, class or module. Variables initialised in if statements, while statements and for statements are available outside the if/while/for statement for use
This is different to many other languages where accessing the variable would throw an exception because of it being out of scope
Just to note, if the if/while/for statement is false and does not execute, a for example would not be initialised and it would throw an error like so:
>>> if False:
... a = 5
...
>>> print a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Related

My question is about the functionality of the 'eval' function when this is included in a function of an imported module

I have created a simple function for reporting current values of variables in some engineering scripts, by passing the variable name in an eval() function. The argument is passed as string then the eval() reads it and reports back the value with some additional info. The function works properly in a single script. However when i am importing the same function from a module i get back an error saying that the variable has is not defined.
I have trying setting it up as a global variable but still get the same problem
def report(name,units = '-',comment ='NC'):
if type(eval(name)) == str:
print('{0:<12}= {1:^10} {2:^5} {3}'.format(name,eval(name),units,comment))
else:
print('{0:<12}= {1:8.3f} {2:^5} {3}'.format(name,eval(name),units,comment))
While trying to use the function from the imported module i get the following
>>>from reporting import*
>>> from shapes import*
>>> Iyy = rec_Iyy(40,60)
>>> report('Iyy')
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
File "C:\Users\vousvoukisi\OneDrive\11.Python\03_myScripts\design_mod\reporting.py", line 8, in report
if type(eval(name)) == str:
File "<string>", line 1, in <module>
NameError: name 'Iyy' is not defined
## while i would expect the outcome to be :
>>> %Run reporting.py
Iyy = 720000.000 - NC

Calling exec, getting NameError though the name is defined

I have a file named file.py containing the following script:
def b():
print("b")
def proc():
print("this is main")
b()
proc()
And I have another file named caller.py, which contains this script:
text = open('file.py').read()
exec(text)
When I run it, I get the expected output:
this is main
b
However, if I change caller.py to this:
def main():
text = open('file.py').read()
exec(text)
main()
I get the following error:
this is main
Traceback (most recent call last):
File "./caller.py", line 7, in <module>
main()
File "./caller.py", line 5, in main
exec(text)
File "<string>", line 10, in <module>
File "<string>", line 8, in main
NameError: global name 'b' is not defined
How is function b() getting lost? It looks to me like I'm not violating any scope rules. I need to make something similar to the second version of caller.py work.
exec(text) executes text in the current scope, but modifying that scope (as def b does via the implied assignment) is undefined.
The fix is simple:
def main():
text = open('file.py').read()
exec(text, {})
This causes text to run in an empty global scope (augmented with the default __builtins object), the same way as in a regular Python file.
For details, see the exec documentation. It also warns that modifying the default local scope (which is implied when not specifying any arguments besides text) is unsound:
The default locals act as described for function locals() below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns.
Would it work for you if you imported and called the function instead?
myfile.py
def b():
print("b")
def proc():
print("this is main")
b()
caller.py
import myfile
myfile.proc()

Why does Python 3 exec() fail when specifying locals?

The following executes without an error in Python 3:
code = """
import math
def func(x):
return math.sin(x)
func(10)
"""
_globals = {}
exec(code, _globals)
But if I try to capture the local variable dict as well, it fails with a NameError:
>>> _globals, _locals = {}, {}
>>> exec(code, _globals, _locals)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-9-aeda81bf0af1> in <module>()
----> 1 exec(code, {}, {})
<string> in <module>()
<string> in func(x)
NameError: name 'math' is not defined
Why is this happening, and how can I execute this code while capturing both global and local variables?
From the exec() documentation:
Remember that at module level, globals and locals are the same dictionary. If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.
You passed in two separate dictionaries, but tried to execute code that requires module-scope globals to be available. import math in a class would produce a local scope attribute, and the function you create won't be able to access that as class scope names are not considered for function closures.
See Naming and binding in the Python execution model reference:
Class definition blocks and arguments to exec() and eval() are special in the context of name resolution. A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods[.]
You can reproduce the error by trying to execute the code in a class definition:
>>> class Demo:
... import math
... def func(x):
... return math.sin(x)
... func(10)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in Demo
File "<stdin>", line 4, in func
NameError: name 'math' is not defined
Just pass in one dictionary.

Unbound method must be called with X instance as first argument (got str instance instead)

I am playing with pyelliptic, but I can't use the method raw_get_ecdh_key defined in the file ecc.py.
Here's my example code:
>>> import pyelliptic
>>> pubkey1="0453eb8248f42b00d057bc1adc724c4c841ec75851d6cd86f56f9bae5223219c7d3c7aff832d2383dfec167327ef38e9bf066690a8f94c055b336a607cebc2dddf".decode('hex')
>>> pubkey2="04678b95e902b04817ba258ecd3dbb2150d83850848a3b8523c11d51b6a9f89b93ea82db74ba82ba44fadb050b35eae8b96f3738e88c7c117227303a528c8df985".decode('hex')
>>> pyelliptic.ECC.raw_get_ecdh_key(pubkey1, pubkey2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method raw_get_ecdh_key() must be called with ECC instance as first argument (got str instance instead)
I searched and found many questions on the subject here. I learned that I need to call ECC() instead of ECC, but it doesn't work any better:
>>> pyelliptic.ECC().raw_get_ecdh_key(pubkey1, pubkey2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/pyelliptic/ecc.py", line 324, in raw_get_ecdh_key
OpenSSL.EC_KEY_free(own_key)
UnboundLocalError: local variable 'own_key' referenced before assignment
I can't figure what's wrong. I checked and the variable own_key should be referenced.
It's a very obvious bug in pyelliptic - if you look at the code, the part where own_key is defined (ecc.py line 301) is wrapped in huge try/finally block. If an exception is raised (and not managed) before line 301 (which is obviously what happened to you), then the execution flow jumps to the finally block, which to tries to reference own_key which at this point has not yet been defined.
You can fix by cloning the git repo and editing the raw_get_ecdh_key function's code so 1. the relevant local variables are defined at the beginning of the function before the try block (setting them to None) and 2. in the finally block these variables are tested against None before trying to use them to free resources, ie (untested):
def raw_get_ecdh_key(self, pubkey_x, pubkey_y):
other_key = None
other_pub_key_x = None
other_pub_key_y = None
other_pub_key = None
own_key = None
own_priv_key = None
try:
# (snip code)
finally:
if other_key is not None:
OpenSSL.EC_KEY_free(other_key)
if other_pub_key_x is not None:
OpenSSL.BN_free(other_pub_key_x)
if other_pub_key_y is not None:
OpenSSL.BN_free(other_pub_key_y)
if other_pub_key is not None:
OpenSSL.EC_POINT_free(other_pub_key)
if own_key is not None:
OpenSSL.EC_KEY_free(own_key)
if own_priv_key is not None:
OpenSSL.BN_free(own_priv_key)
And then run the the unittests (eventually adding some tests for this case) and submit a pull request to the author.
Note that you'll have applied this (untested) fix you should have another error - the one that sent you in the finally block at first - but at least you should then have more infos about why the call failed.

Python handling the name error exception

I wrote a small code and tried to handle the name error exception.
I want to print a custom message even if there is an exception, but it is showing the complete the trace back.
#!/usr/bin/python -tt
import logging
def equaldigits(a, b):
logging.basicConfig(filename='run.log',level=logging.INFO)
try:
c = a - b
logging.info('%s is the difference between both the digits', str(c))
print c
return c
except NameError as e:
c = 'Unable to successfully complete execution'
logging.info(c)
print c
#return c
def main():
print '\n1st call'
equaldigits(10, 10)
print '\n2nd call'
equaldigits(1, 0)
print '\nException call'
equaldigits(a, 0)
# Standard boilerplate to call the main() function.
if __name__ == '__main__':
main()
This is the console output
1st call
0
2nd call
1
Exception call
Traceback (most recent call last):
File "./sut.py", line 28, in <module>
main()
File "./sut.py", line 24, in main
equaldigits(a, 0)
NameError: global name 'a' is not defined
In your attempt to catch an exception, you wrote equaldigits(a, 0). The interpreter sees the a and thinks it is a variable that isn't there, thus throwing the uncaught exception. In order to test your try/catch, you need to pass the letter a, like so
equaldigits('a', 0)
^ ^ note the quotes
The problem isn't happening within your equaldigits function where you have your logging information.
Its happening in your main function when the interpreter tries to pass the value of a to equaldigits. The variable a doesn't exist within the local scope of main, thus Python tries to find a global variable named a. Since it doesn't see one, it throws a NameError.
Your error is caused by the fact that a is not defined when you call equaldigits, the execution doesn't get to the try/except clause inside the function.
when you change
a - b
to
a - d
inside the function you'll see that your try/except works fine

Categories

Resources