Alternatives to storing variables globally - python

I am starting out with Python now, working on the Learn Python The Hard Way, on exercise 36. I want to create a good game, and learn some more techniques before moving on with the next exercise. I haven't approached the subject of Classes and Object yet, yet I want to see if I can do something that would be a bit more complex than a standard first 'choose-your-own-adventure' game.
I want to collect four different keys in 'Ganon's Lair', and then use those four keys to open a door in the 'main-hall'. I already have quite some of it worked out (not elegantly), but I still need to figure out how to store keys without them getting erased. An unelegant way is to assign them as global variables, such as I do here.
def grass_room():
global key1
grass_instructions = """As you enter the lair of the grass room, you see
a spider hanging on the ceiling, with his
big eye focused on you. You could grab your 1.slingshot,
or perhaps 2.make a run for the green-coloured key under his
tail?"""
print grass_instructions
gohma_boss = raw_input("> ")
if gohma_boss == "1":
print "You shoot him in the eye, he falls down and dies. You grab the key, and return."
key1 = True
main_hall("not_empty")
else:
print die("You die.")
main_hall("not_empty")
Any suggestions for different ways to 'save' this key across functions, besides making them global?

If you want some variable or variables to be shared between functions, there are a few ways to do it:*
Pass each variable's value into every function as an argument, and return it from every function as part of a return tuple.
Wrap all of the values up in some structure, like a dict that you can look each thing up in by name, so you only have one value to pass and return.
Make the values attributes of an object, and turn the functions into methods of that object's type.
Use a closure, which I won't bother to explain because I'm sure you haven't learned about closures yet.
Use globals.
The Pythonic way to do this is definitely #3, but you haven't learned about classes yet. In that case, I'd just use #5, as you're already doing.
And when you learn about classes, coming back and modifying this script to use a class instead will be a great exercise.
* In fact, under the covers, options 3-5 are all pretty much syntactic sugar for option 2… but don't worry about that.

You could make your key datatype a list or a dict and pass the key list/dict into each key-changing function. Since lists and dicts are mutable, changes made to the key list/dict in one function will be seen in the calling scope and in any subsequent functions that key is passed to.

Related

Using a global variable versus calling the function that returns said variable repeatedly

I have a Python module which consists of a number of different functions.
Say my first function returns a variable which I want to use twice in the second and the second returns a variable which I want to use four times in the third function ( . . . and so on).
Is it better to declare the variables that I will want to use throughout the entire module as global and then call the function that returns said variable once to define it globally rather than to call functions more than once in order to use the variables they return?
Am I correct in saying that this is a trade-off between safety (not using global variables) and efficiency (not executing each function more than once if possible)?
def fn_for_reading_file():
# (Insert code for prompting user for filename, opening and reading file)
global file_as_string
# (Insert code for assigning user's file to file_as_string)
return file_as_string
fn_for_reading_file()
def extract_data_from_string():
global my_list = []
# (Insert code for going through return_file_as_string and appending data to my_list)
return my_list
extract_data_from_string()
def another_fn():
# (Insert code which uses file_as_string and my_list)
return fn_output
another_fn()
I would try to reframe the problem you're thinking about. If you're only thinking in terms of functional programming then yes, you're correct in that safety vs. more code is the basic trade off you're looking at.
However, there are a number of ways to get around your dilemma by reframing the problem. I obviously don't know what your code looks like, but it might be meaningful to think about building this functionality into a class. Rather than using global variables, set those values as class attributes with appropriate getters/setters, and then structure the module such that your functions become methods.

Accessing specific values of a loop-created dictionary in a Python function

I am very new to Python and as an exercise I tried solving a basic finance exercise using code. My objective is to get a dictionary of spot rates and then a dictionary of discount rates calculated from those. I had thought to something like this:
discountrates={}
def discountrates(n):
spotrates={}
for x in range(1,n+1):
spotrates['s'+str(x)]=float(input('What is s'+str(x)+'? (not in percentage)'))
for y in range(1,n+1):
discountrates['d(0,'+str(y)+')']= 1/((1+float(spotrates['s'+str(y)]))**y)
for key, value in discountrates.items():
print (key, value)
Now the problem is that dictionary items cannot be accessed in a function. When I looked in your forum, I found solutions for unpacking the dictionary but that does not work in my case because I need to access a specific element of the dictionary, whose name cannot be fully specified (as I have seen in the Python manual) because it's part of a loop, in order for the formula to work without having to manually insert anything else. I used a dictionary in the first place to create names that were automatically generated but now I can't seem to get the information out of it.
What is the best solution?
Thanks in advance for the help. It's been driving me crazy.
It's because you called your global variable discountratesdict not discountrates (which is the name of your function).
I suggest you don"t name your dictionary like your function since the later will overwrite the former. In line 1 you say discountrates is an empty dict, in line 2 you say discountrates is a function object. You need to give them different names in python if they are on the same scope.
Furthermore why do you need discountrates to be global? would you like to keep old rates if n is smaller than a previous n? For performance I suggest you combine the two loops. Besides that there is no reason why the second loop can't read for x ... as well since zou don't use x anymore anyway. As a further hint, if you come to the conclusion, that a global is the only way it might help to add global discountratesdict, so it is easier to spot that a global is intended here, even though this is not necessary in your particular case since the []-operator needs an object and thus it already refers to your global.
Putting all this together yields:
discountratedict={}
def discountrates(n):
global discountratedict
spotrates={}
for x in range(1,n+1):
spotrates['s'+str(x)]=float(input('What is s'+str(x)+'? (not in percentage)'))
discountratedict['d(0,'+str(x)+')']= 1/((1+float(spotrates['s'+str(x)]))**x)
for key, value in discountratedict.items():
print (key, value)

Referencing Python list elements without specifying indexes

How can I store values in a list without specifying index numbers?
For example
outcomeHornFive=5
someList = []
someList.append(outComeHornFive)
instead of doing this,
someList[0] # to reference horn five outcome
how can i do something like this? The reason is there are many items that I need to reference within the list and I just think it's really inconvenient to keep track of which index is what.
someList.hornFive
You can use another data structure if you'd like to reference things by attribute access (or otherwise via a name).
You can put them in a dict, or create a class, or do something else. It depends what kind of other interaction you want to have with that object.
(P.S., we call those lists, not arrays).
Instead of using a list you can use a dictionary.
See data types in the python documentation.
A dictionary allows you to lookup a value using a key:
my_dict["HornFive"] = 20
You cannot and you shouldn't. If you could do that, how would you refer to the list itself? And you will need to refer to the list itself.
The reason is there are many items that i need to reference within the list and I just think it's really inconvenient to keep track of which index is what.
You'll need to do something of that ilk anyway, no matter how you organize your data. If you had separate variables, you'd need to know which variable stores what. If you had your way with this, you'd still need to know that a bare someList refers to "horn five" and not to, say, "horn six".
One advantage of lists and dicts is that you can factor out this knowledge and write generic code. A dictionary, or even a custom class (if there is a finite number of semantically distinct attributes, and you'd never have to use it as a collection), may help with the readability by giving it an actual name instead of a numeric index.
referenced from http://parand.com/say/index.php/2008/10/13/access-python-dictionary-keys-as-properties/
Say you want to access the values if your dictionary via the dot notation instead of the dictionary syntax. That is, you have:
d = {'name':'Joe', 'mood':'grumpy'}
And you want to get at “name” and “mood” via
d.name
d.mood
instead of the usual
d['name']
d['mood']
Why would you want to do this? Maybe you’re fond of the Javascript Way. Or you find it more aesthetic. In my case I need to have the same piece of code deal with items that are either instances of Django models or plain dictionaries, so I need to provide a uniform way of getting at the attributes.
Turns out it’s pretty simple:
class DictObj(object):
def __init__(self, d):
self.d = d
def __getattr__(self, m):
return self.d.get(m, None)
d = DictObj(d)
d.name
# prints Joe
d.mood
# prints grumpy

iterate a list when list name is dynamically generated

How do I iterate through a list whose name will be dynamically generated?
boneList_head =['def_neck', 'def_armbase']#hard coded list
itemType='head'# result of a user button press
...
def selectBones():
global itemType
bones =('boneList_'+itemType)# evaluates as a string , not name of a list
for bone in bones:
cmds.select(bone, tgl=True)
the problem is bones is getting evaluated as a string, when I need it to evalute as the name of a list.
Dynamically generating variable names is almost always a bad approach. Use a dictionary!
bonedict = {'boneList_head': ['def_neck', 'def_armbase']}
itemType='head'
def selectBones(itemType):
bones = bonedict['boneList_' + itemType]
for bone in bones:
cmds.select(bone, tgl=True)
Please ignore my previous answer (visible in my edit history) which was stupid -- boneheaded, even. But I blame its stupidity on dynamic variable name generation!
Let me elaborate on why dynamic variable name generation is a bad idea.
Because dynamic variable generation masks variable name definitions. It's hard to tell what has been defined and what hasn't, so it's easy to accidentally redefine a variable. This is a major source of potential bugs.
Because dynamic variable manipulation hides state changes under another layer of obfuscation. To some degree, this is true anytime you create a dictionary or a list. But one expects lists and dictionaries to demand a little extra thinking. Variable names, on the other hand, should be dead simple. When variable definitions and redefinitions require deep thought to understand, something is wrong.
Because dynamic variable generation pollutes the namespace. If you have so many variables that you have to automatically generate them, then they should live in their own namespace, not in the locals of a function, and definitely not in the global namespace. In his style guide for the linux kernel, Linus Torvalds advises that if a function has more than 5-10 local variables, you're doing something wrong.
Because dynamic variable generation contributes to high coupling, which is a bad thing. If you assign to values to a dictionary, you can pass that dictionary back and forth until the cows come home, and all anyone has to know about is that dictionary. If you dynamically create variable names in the global namespace of a module, then if another module wants to access those variable names, it has to know all about the way they were generated, what other variables in that module are defined, and so on. Also, passing the variables around becomes much more complex -- you have to pass around a reference to the module itself, probably using sys.modules or other questionable constructs.
Because dynamic variable generation is ugly. eval looks neat and clean, but it really isn't. It can do anything. Functions that can do anything are bad, because you can't tell at first glance what they're doing here. A well-defined function does one thing, and does it well; that way, whenever you see that function, you know exactly what's happening. When you see eval, literally anything could be happening. In this sense, eval is like goto. The problem with goto is not that you can't use it correctly; it's that for every possible correct use of goto, there are 500,000,000 terrifyingly wrong ways to use it. I won't even discuss the security problems here, because in the end, that's not the real problem with eval.
I agree with the other comments that your approach is probably not the best. But the following should work:
bones = eval('boneList_' + itemType)
This will run the python interpreter on "boneList_head", and return the list.
NOTE: As Adam Mihalcin mentioned in the comments, you should be very careful about only running eval on data that you trust or have validated. A malicious user could inject arbitrary code into the itemType variable to access the os, etc.
This is an ugly hack, but it works...(of course, you need to get the correct module)
import sys
boneList_head =['def_neck', 'def_armbase']
itemType='head'
...
def selectBones():
global itemType
bones=vars(sys.modules["__main__"])['boneList_'+itemType]
for bone in bones:
cmds.select(bone, tgl=True)
This really isn't different than what other people are saying however-- We're using vars to construct a dictionary to get the list you want -- why not just pass a dictionary (or the correct list) to the function selectBones in the first place?

Question about whether to include something in the __init__() method

I am new to OOP and hence, am looking for suggestions on good practice for coding something where the following issue arises.
I am defining a Seller(a, b, c, d) class. There are many attributes of this class, two of which are, mostRecentProfit and profitHistory. However, values of these two are not known when the class is initialized. Some other steps in the program have to be executed before these are realized. My questions is:
In the __init__(a, b, c, d) of the seller class, should I write
self.mostRecentProfit = None
self.profitHistory = []
or, should I not define these at all in the __init__ method. The reason former appears attractive to me is that by looking at the __init__() method, I can know all the attributes for the class. However, that may not be a good reason for doing this. Any suggestions would be appreciated.
Thank you.
Defining the attributes in __init__() makes the code better for when someone who has not seen the code has to start working with it. It can be confusing when a class starts accessing an attribute that doesn't seem to exist at first.
Also, since one of your default values is a list instead of None, initializing it means you can always treat the attribute as a list and never have to worry about it's state.
I would define them. In my experience, not doing so when the code dealing with the instances makes frequent references to those properties, means you end up forever typing if object.profitHistory: before looping etc. With an empty list there, you can skip those conditions. And as you say, it makes it much more legible.
I would define them all in the __init() method because that would not only document what they all normally were, but if you define their default values to all be something valid, allow most of the rest of your code to easily process instances of the class even if these attributes never get updated.
So, in your example, that would mean initializing self.mostRecentProfit to 0 or perhaps 0.0 rather than None. Doing this would allow it to be used as a number without checking for it's existence with a value not equal to None before each reference to it or wrapping each of them in a try/except block to handle the cases where they were never explicitly set to another value.

Categories

Resources