NameError when running in `main` module - python-3.x

I am attempting to grab all the .md files recursively within files.To complish it,I define a recursive function which run in a main module.
The code:
import os
def walk(dirname):
for name in os.listdir(dirname):
path = os.path.join(dirname, name)
if os.path.isfile(path):
if '.md' in path:
path_list.append(path)
else:
walk(path)
return path_list
def main():
dir = '/Users/Documents/Diary'
path_list = []
path = walk(dir)
if __name__ == '__main__':
main()
When running, it reports:
NameError: name 'path_list' is not defined
However, if running without of main(), it works:
In [80]: path_list = []
...: def walk(dirname):
...: for name in os.listdir(dirname):
...: path = os.path.join(dirname, name)
...: if os.path.isfile(path):
...: if '.md' in path:
...: path_list.append(path)
...: else:
...: walk(path)
...: return path_list
output:
dir = '/Users/Documents/Diary'
walk(dir)
Out[81]:
['/Users/Documents/Diary/py4.1.If_statements.md',
'/Users/Documents/Diary/pyName_and_object:.md',
...]
I have no idea what's the bug.

Your second option declares path_list as a global variable, so it is known in all functions.
You could also declare it in the walk() function, the only place where it is needed. But, as commented, since that function is called recursively, that would reset the list every time, instead of aggregating the results.
See more at "Notes on Python variable scope".
Global variables are accessible inside and outside of functions.
Local variables are only accessible inside the function.
If I set a variable in a function with the same name as a global variable, I am actually creating a new local variable.

The problem here is that of scope of object.
When you make a main() function, you are declaring path_list in the local scope of main(). Hence it is not available to walk() function.
You have to pass path_list as an argument to walk or declare it globally as you did latter to make it available. As of current, path_list is out of scope of walk().

You need to declare path_list in the walk function, as it is the function that is operating on it. You get an error because you don't have any path_list pre-declared in that function.
Moreover, you don't need to define any path_list in the main function because it does not operate on it an doesn't need it.
When you declare it globally, it is available to all the functions, even the walk function. It uses that global variable and you don't get any error.

Related

How to Use Global List in different function in Python?

I have a function: instanceDetails() in which I am trying to use global variables, global lists are declared in just after class definition.
class InstanceDescribe:
#both variables are global
all_instances_health = [0] #stores some ids such as ip-address
all_instances_health_index = [0] #against the IP address stores health of server
#some code goes here
def instanceDetails():
#some code goes here to find value of instance_role, instanceId and much more which is used as argument
if instance_role=='dispatcherpub' or instance_role=='dispatcheraut':
if instanceId in **all_instances_health**: #checking IP address is in global variable list
print("Yes instance in list, so not calling target gp health function")
position = **all_instances_health.index(instanceId)**
instanceDetailsresult_dict["Health Status"] = **all_instances_health_index[position]**
else: #if IP address is not there then calling another function and this function returns values
print("No instance is not in list, so calling target gp health function")
return_list = target_object.target_gp_health(instanceId,autoscaling_group_name,cross_account_cred_list)
instanceDetailsresult_dict["Health Status"] = return_list[0]
**all_instances_health** = return_list[1]
**all_instances_health_index** = return_list[2]
Now, could you please tell me, where I have to use global keyword so that my function starts working.
I have tried so many things but getting error such as:
name 'all_instances_health' is used prior to global declaration
and after some putting global keyword with list name under if and else block, am getting this error also:
name 'all_instances_health' is not defined
NOTE: other functionalities are working properly
Use it at the start of the functions. I tried that and it worked.
def instanceDetails():
global var1
global var2
...

Parameterize function name in Python

I have a generic function for which I would like the name to change based on the value of predefined variable. Not sure this is possible in Python:
n = 'abc'
def f{n}(x):
print(x)
f{n}(3)
In the above, the function name will end up becoming "fabc"?
It is possible using a code-generation + exec pattern. It is also possible by instantiating a types.FunctionType instance directly. But a much simpler solution would be to leave the generic name, and then inject an alias (another reference to the same function object) for the dynamic name into whichever namespace you want.
>>> def generic_function(x):
... print(x ** 2)
...
>>> dynamic_name = "func_abc"
>>> globals()[dynamic_name] = generic_function
>>> func_abc(3)
9
To inject in some other module namespace that would be a setattr call:
setattr(other_mod, dynamic_name, generic_function)
You could also rewrite the function's __name__ attribute if you wanted to, but there's probably not much point.
You could do it with something like the below:
def f(n):
exec(f"def f{n}(x): print(x)")
return eval(f"f{n}")

Indirect variable modification not working withing class, but works outside of class (Python 3.7)

Since I get variable definitions from an external text file i need to modify my local variables indirectly.
What I want to do is basically working, but not once I try to implement it within a class.
X = "0"
vars()["X"]+="1"
print(X) #gives "01" as expected
class Test:
def __init__(self):
x = "0"
vars()["x"]+="1"
self.x = x
test = Test()
print(test.x) # gives "0", but why?
While the procedual code snip produces the expected result "01", the code inside the class does not ("0"). Why?
Here is what the manuals says about vars (https://docs.python.org/3/library/functions.html#vars):
Return the dict attribute for a module, class, instance, or any other object with a dict attribute.
Objects such as modules and instances have an updateable dict attribute; however, other objects may have write restrictions on their dict attributes (for example, classes use a types.MappingProxyType to prevent direct dictionary updates).
Without an argument, vars() acts like locals(). Note, the locals dictionary is only useful for reads since updates to the locals dictionary are ignored.
In other words, when writing directly you are at the module level that has a writable __dict__, not so when you are inside a method.
Although this is bad practice and is not recommended, but this can help you get going:
class Test:
def __init__(self):
self.x = '0'
self.__dict__['x']+='1'
test = Test()
print(test.x) # gives "01"
You are mixing a lot of things here. First I will comment on your code:
X = "0"
vars()["X"]+="1"
print(X) #gives "01" as expected
class Test:
def __init__(self):
global X
x = "0" # this is a local variable inside __init__()
vars()["X"]+="1" # vars() only shows local variables, will lead to an key error because vars()['X'] does not exist
self.x = x # assign member variable with value of local variable
test = Test()
print(test.x) # gives "0", because you just assigned self.x = x, which is "0"
You could use the global keyword to make X visible inside init() but this is considered bad practice.
X = "0"
class Test(object):
def __init__(self):
global X
self.x = X + "1"
test = Test()
print(test.x) # gives "01"
You better initalize Test with the variable needed:
X = "0"
class Test(object):
def __init__(self, myvar: str):
self.x = myvar + "1"
test = Test(X)
print(test.x) # gives "01"
The documenation for vars() without an argument says:
Without an argument, vars() acts like locals(). Note, the locals dictionary is only useful for reads since updates to the locals dictionary are ignored.
But that is incomplete. The documentation for locals() says:
Note that at the module level, locals() and globals() are the same dictionary.
And changes to the dictionary returned by globals() are not ignored as it is the actual symbol table:
Return a dictionary representing the current global symbol table. This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called).
So by this really strange design, vars() when used at module level actually does return the writeable symbol table -- and anywhere else, it uses a view on which writes are ignored.
None of this really matters, as using these functions is usually a good sign that you're doing something wrong, but still, here it is.

locals() built-in method in Python3 returns the variable in the global namespace

name = "Lenin Mishra"
def home():
name = "Sonu"
print(name)
home()
print(locals())
When I run the above code, Python returns me a dictionary containing the variable name, which has a value of Lenin Mishra.
{'__name__': '__main__', '__doc__': '\nExperimenting with scope\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10323b400>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/Users/leninmishra/Desktop/python_basics/scope.py', '__cached__': None, 'name': 'Lenin Mishra', 'home': <function home at 0x101db5e18>}
But as far as I understand, the variable name which has been assigned the value of Lenin Mishra is in the global scope.
Why is this happening?
In your code, locals() executed at global scope, so you got that output.
If you execute that locals() as part of another function, you could get the local variables to that specific function's scope.
For example, if you write your code as below, you could get out-put as empty brackets.
python works based on indentation.
Sample-code
def myfun():
print(locals())
myfun()
Output
{}
Sample code2
def home():
name = "Sonu"
print(locals())
home()
output
{'name': 'Sonu'}
This sample 2 code explains only name variable is available in its local scope.
Help of locals function says as follows:
In [1]: ?locals
Signature: locals()
Docstring:
Return a dictionary containing the current scope's local variables.
NOTE: Whether or not updates to this dictionary will affect name lookups in
the local scope and vice-versa is *implementation dependent* and not
covered by any backwards compatibility guarantees.
Type: builtin_function_or_method

How do I insert a file into a string? (python3)

I have been trying to make a program that requires reading from a file and then making the string inside the file part of a string in the program. I have written an example of what I do:
gameinfo = [0,0]
def readsave(savefile):
"Reads a file and adds its statistics to variables"
filename = savefile
with open(filename) as file_object:
gameinfo = file_object.readlines()
print(gameinfo)
readsave('gamesave.txt')
print (gameinfo)
But whenever I run this code, all I seem to get is:
['thisworks\n', '7']
[0, 0]
The [0,0] string is what I am trying to change to ['thisworks\n, 7'], however it only changes inside the function. Is there any way which I can make this change global?
The problem here is scope, the gameinfo variable in the function is a local, not a global. You can declare it global, or pass gameinfo around as a parameter. Generally, I avoid global declarations as they can get confusing. I'd recommend passing gameinfo around:
def readsave(savefile, gameinfo=[0,0]): # Declare it as a default to the function.
"Reads a file and adds its statistics to variables"
with open(savefile) as file_object: # No need to rename this.
gameinfo = file_object.readlines()
return gameinfo # Return it so it escapes the scope of this function.
gameinfo = readsave('gamesave.txt') # Save it.
print(gameinfo) # Print it.
Variables are not shared in functions which means you define gameinfo = [0,0] but you are never actually getting that variable in the function. I you want to save in gameinfo you need to use return or global. global will make it possible to share variables inside the function and outside however this is considered bad practice so don't use it.
To use return simply put it in your function. Always make sure you have only one variable, string, integer returning once per call.
Here is your example rewritten to include the return statement I mentioned above:
gameinfo = [0,0]
def readsave(savefile):
"Reads a file and adds its statistics to variables"
filename = savefile
with open(filename) as file_object:
gameinfo = file_object.readlines()
print(gameinfo)
return gameinfo
gameinfo = readsave('gamesave.txt')
print (gameinfo)
You have also made a few other mistakes:
"Reads a file and adds its statistics to variables" is not a comment. Use """my text here""" (triple quotes) or #my text here to insert comments.
All these things you will learn as you read the Python tutorial. Here is one illustrating the use of return.

Resources