I'm a bit confused about a code in the book "Learning Python", p. 539.
As far as I know assignments within a function are only in this local scope. So if I want to change a global one I first have to declare it global. But why does the following code change the builtin.open() to custom completely once called?
import builtins
def makeopen(id):
original = builtins.open
def custom(*pargs, **kargs):
print('Custom open call %r: ' % id, pargs, kargs)
return original(*pargs, **kargs)
builtins.open = custom
If I call makeopen('spam') and a F = open('text.txt') afterwards I get the custom call. So the builtin.open() has been changed in the whole script after the makeopen('spam'). Why?
And if I would make some more makeopen('xx') one builtin.open('text.txt') would print the custom call for every created makeopen. Why?
Comparing this code to
x = 99
def changing():
x = 88
changing()
print(x)
doesnt even help me. Isn't it the same but with an x instead of builtin.open()?
A variable is considered local if you assign to it anywhere in the function, unless you declare it global.
In your first piece of code, you never assign anything to builtins, so it's not considered local. You just change one of its attributes, open.
The rule is respected!
In your second piece of code, you assign something to x in x = 88, so it is considered local.
When you call makeopen, you replace the original, global open with custom. custom, when executed, prints its name and calls the original open.
If you call makeopen a second time, it will create a second, different custom function, and make the name builtins.open refer to it. When you call this function, it will print its name, then call original, which is what builtins.open referred to when it was created - and that is your first custom function, which will print its name and call the original open.
So, successive calls to makeopen create a chain of functions, and calling open will make each of them run and call its predecessor.
Related
I am unsure of why the variable totalspeed variable is not being passed correctly to the function startgame as the startgame function is called after the gettotalspeed function.
Exerpt from call function:
gettotalspeed(party_ids)
NoOfEvents=0
startgame(party_ids,totalspeed,distance,NoOfEvents)
Functions
def gettotalspeed(party_ids):
#Get selected party members IDS
print(party_ids)
#Obtain Speeds
ids_string = ','.join(str(id) for id in party_ids)
mycursor.execute("SELECT startspeed FROM characters WHERE CharID IN ({0})".format(ids_string))
myspeeds=mycursor.fetchall()
totalspeed=0
for speedval in myspeeds:
totalspeed=totalspeed + speedval[0]
print("totalspeed is: ",totalspeed)
return totalspeed
def startgame(party_ids,totalspeed,distance,NoOfEvents):
#Check if game end
print(totalspeed)
while distance!=0:
#Travel...
distance=distance-totalspeed
NoOfEvents=NoOfEvents+1
#Generate Random Encounter
genevent(NoOfEvents)
return NoOfEvents
Error Produced:
NameError: name 'totalspeed' is not defined
Outputs (ignoring party_ids)
totalspeed is: 15
I suspect that your problem is self-evident from the main program:
gettotalspeed(party_ids)
NoOfEvents=0
startgame(party_ids,totalspeed,distance,NoOfEvents)
Of the variables you pass to your functions, only NoOfEvents is defined. party_ids, totalspeed, and distance have no definitions.
Work through a tutorial on Python scoping rules. Most of all, note that a function defines a scoping block. Variables inside the function are reclaimed when you leave the function; their names do not apply outside of that block. Your posted program has three independent totalspeed variables.
You forgot to make totalspeed a global variable like global totalspeed in your gettotalspeed() function. You might also be confused about what return does. If you wanted to do it the "proper" way, you could do totalspeed = gettotalspeed(party_ids). Hope this helps!
True or False
If a function is defined but never called, then Python automatically detects that and issues a warning
One of the issues with this is that functions in Python are first class objects. So their name can be reassigned. For example:
def myfunc():
pass
a = myfunc
myfunc = 42
a()
We also have closures, where a function is returned by another function and the original name goes out of scope.
Unfortunately it is also perfectly legal to define a function with the same name as an existing one. For example:
def myfunc(): # <<< This code is never called
pass
def myfunc():
pass
myfunc()
So any tracking must include the function's id, not just its name - although that won't help with closures, since the id could get reused. It also won't help if the __name__ attribute of the function is reassigned.
You could track function calls using a decorator. Here I have used the name and the id - the id on its own would not be readable.
import functools
globalDict = {}
def tracecall(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
global globalDict
key = "%s (%d)" % (f.__name__, id(f))
# Count the number of calls
if key in globalDict:
globalDict[key] += 1
else:
globalDict[key] = 1
return f(*args, **kwargs)
return wrapper
#tracecall
def myfunc1():
pass
myfunc1()
myfunc1()
#tracecall
def myfunc1():
pass
a = myfunc1
myfunc1 = 42
a()
print(globalDict)
Gives:
{'myfunc1 (4339565296)': 2, 'myfunc1 (4339565704)': 1}
But that only gives the functions that have been called, not those that have not!
So where to go from here? I hope you can see that the task is quite difficult given the dynamic nature of python. But I hope the decorator I show above could at least allow you to diagnose the way the code is used.
No it is not. Python is not detect this. If you want to detect which functions are called or not during the run time you can use global set in your program. Inside each function add function name to set. Later you can print your set content and check if the the function is called or not.
False. Ignoring the difficulty and overhead of doing this, there's no reason why it would be useful.
A function that is defined in a module (i.e. a Python file) but not called elsewhere in that module might be called from a different module, so that doesn't deserve a warning.
If Python were to analyse all modules that get run over the course of a program, and print a warning about functions that were not called, it may be that a function was not called because of the input in this particular run e.g. perhaps in a calculator program there is a "multiply" function but the user only asked to sum some numbers.
If Python were to analyse all modules that make up a program and note and print a warning about functions that could not possibly be called (this is impossible but stay with me here) then it would warn about functions that were intended for use in other programs. E.g. if you have two calculator programs, a simple one and an advanced one, maybe you have a central calc.py with utility functions, and then advanced functions like exp and log could not possibly be called when that's used as part of simple program, but that shouldn't cause a warning because they're needed for the advanced program.
I have a utilities.py file for my python project. It contains only util functions, for example is_float(string), is_empty(file), etc.
Now I want to have a function is_valid(number), which has to:
read from a file, valid.txt, which contains all numbers which are valid, and load them onto a map/set.
check the map for the presence of number and return True or False.
This function is called often, and running time should be as small as possible. I don't want to read open and read valid.txt everytime the function is called. The only solution I have come up with is to use a global variable, valid_dict, which is loaded once from valid.txt when utilities.py is imported. The loading code is written as main in utilities.py.
My question is how do I do this without using a global variable, as it is considered bad practice? What is a good design pattern for doing such a task without using globals? Also note again that this is a util file, so there should ideally be no main as such, just functions.
The following is a simple example of a closure. The dictionary, cache, is encapsulated within the outer function (load_func), but remains in scope of the inner, even when it is returned. Notice that load_func returns the inner function as an object, it does not call it.
In utilities.py:
def _load_func(filename):
cache = {}
with open(filename) as fn:
for line in fn:
key, value = line.split()
cache[int(key)] = value
def inner(number):
return number in cache
return inner
is_valid = _load_func('valid.txt')
In __main__:
from utilities import is_valid # or something similar
if is_valid(42):
print(42, 'is valid')
else:
print(42, 'is not valid')
The dictionary (cache) creation could have been done using a dictionary comprehension, but I wanted you to concentrate on the closure.
The variable valid_dict would not be global but local to utilities.py. It would only become global if you did something like from utilities import *. Now that is considered bad practice when you're developing a package.
However, I have used a trick in cases like this that essentially requires a static variable: Add an argument valid_dict={} to is_valid(). This dictionary will be instantiated only once and each time the function is called the same dict is available in valid_dict.
def is_valid(number, valid_dict={}):
if not valid_dict:
# first call to is_valid: load valid.txt into valid_dict
# do your check
Do NOT assign to valid_dict in the if-clause but only modify it: e.g., by setting keys valid_dict[x] = y or using something like valid_dict.update(z).
(PS: Let me know if this is considered "dirty" or "un-pythonic".)
I am new to python and have been shown 'drivers' for running functions without entering them into the command line,
I don`t understand the concept of drivers or how to type them out correctly, any kind of feedback on how to use them would be great!.
What I don't understand is how entering the function makeGreyscaleThenNegate(pic) can call the def function makeGreyscaleThenNegate(picture): when the input values are different (pic) versus (picture). (I guess this is because I don't know how the 'driver' function works.)
Here is what i've been shown
def driverGrey():
pic=makePicture(pickAFile())
repaint(pic)
makeGreyscaleThenNegate(pic)
repaint(pic)
def makeGreyscaleThenNegate(picture):
for px in getPixels(picture):
lum=(getRed(px)+getGreen(px)+getBlue(px)/3
lum=255-lum
setColor(px,makeColor(lum,lum,lum))
My belief is for this to work,(pic) would already have been named/defined prior to creating the 'driver' function? I just am not sure how (pic) and (picture) are referring to the same file, or am I completely mis-interpreting this..
This is really CS101 and nothing Python-specific actually. A function is a name for a code snippet. The names of the variables you pass as arguments to the function and the names of the arguments in the function are totally unrelated, they are just names. What happens in the above snippet:
def driverGrey():
pic=makePicture(pickAFile())
# 1. call a function named 'pickAFile' which I assume returne a file or filename
# 2. pass the file or filename to the function named 'makePicture' which obviously
# returns a 'picture' object (for whatever definition of a 'picture object')
# 3. binds the 'picture object' to the local name 'pic'
(snip...)
makeGreyscaleThenNegate(pic)
# 1. pass the picture object to the function named 'makeGreyscaleThenNegate'.
#
# At that time we enter the body of the 'makeGreyscaleThenNegate' function,
# in which the object known as 'pic' here will be bound to the local
# name 'picture' - IOW at that point we have two names ('pic' and 'picture')
# in two different namespaces ('driverGrey' local namespace and
# 'makeGreyscaleThenNegate' local namespace) referencing the same object.
#
# 2. 'makeGreyscaleThenNegate' modifies the object.
#
# 3. when 'makeGreyscaleThenNegate' returns, it's local namespace is destroyed
# so we only have the local 'pic' name referencing the picture object,
# and the control flow comes back here.
(snip...)
pic and picture are just names or labels. You can call a chunk of data whatever you want. For example, a Spaniard might call a bottle of milk "leche" whereas a Frenchman might call it "lait".
The same thing applies to Python. You have some sort of "picture" object, and throughout your program, you're calling it by different names. In the driverGray function, you call it pic, and in the makeGrayscaleThenNegate function, you call it picture. Different name, same object.
If I were to do this:
pic = makePicture(pickAFile())
b = pic
c = b
...both pic, b, and c are all referring to the exact same "thing". If I make a change to b by doing something like b.var = 13, both c and pic will change too.
(Note: if you did something like c = 1, then you're saying that c now means a number, rather then a picture object. The pic and b variables are unaffected.
Here's a metaphor: if somebody were to poison the milk, then it doesn't matter what the Spaniard or the Frenchman calls the milk -- it's poisoned, regardless of the particular name.
In your case, when you do makeGreyscaleThenNegate(pic) inside the first function, you are saying that you "pass in" a picture object (which you happen to call pic). The makeGrayscaleThenNegate function is defined as def makeGreyscaleThenNegate(picture):. This means that the first argument that is passed in will be called "picture" for the duration of that function.
I know this kind of question gets asked all the time but either i've been unable to come across the answer i need, or i've been unable to understand it when i did.
I want to be able to do something like:
spam = StringVar()
spam.set(aValue)
class MyScale(Scale):
def __init__(self,var,*args,**kwargs):
Scale.__init__(self,*args,**kwargs)
self.bind("<ButtonRelease-1>",self.getValue)
self.set(var.get())
def getValue(self,event):
## spam gets changed to the new value set
## by the user manipulating the scale
var.set(self.get)
eggs = MyScale(spam,*args,**kwargs)
eggs.pack()
Of course, i get back "NameError: global name 'var' is not defined."
How do i get around the inability to pass arguments to getValue? I've been warned against using global variables but is that my only option? Is it setting up a separate scale class for each variable i want to change? I get the feeling i'm missing something thats right under my nose...
edit:
is this what you mean?
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
File "C:\...\interface.py", line 70, in getValue
var.set(self.get)
NameError: global name 'var' is not defined
Sorry, I've only been programming a month and some of the jargon still escapes me.
Please give this a shot.
Lots of example code out there generously uses globals, like your "var" variable.
I have used your var argument to act as a pointer back to the original spam object; assigned to self.var_pointer within the MyScale class.
The code below will change the value of 'spam' (and 'eggs') on the scale's ButtonRelease.
You can check out the value by typing eggs.get() or spam.get() to see the changed value.
from Tkinter import *
root = Tk()
aValue = "5"
spam = StringVar()
spam.set(aValue)
class MyScale(Scale):
def __init__(self,var,*args,**kwargs):
self.var_pointer = var
Scale.__init__(self,*args,**kwargs)
self.bind("<ButtonRelease-1>",self.getValue)
self.set(var.get())
def getValue(self,event):
## spam gets changed to the new value set
## by the user manipulating the scale
self.var_pointer.set(self.get())
eggs = MyScale(spam)
eggs.pack(anchor=CENTER)
Let's look at this method function
def getValue(self,event):
## spam gets changed to the new value set
## by the user manipulating the scale
var.set(self.get)
The var.set(self.get) line has exactly two local variables available:
self
event
The variable var is not local to this method function. Perhaps it was used elsewhere in the class or script, but it's not local here.
It may, possibly, be global, but that's a bad practice.
I'm not sure why you'd think the variable var would be known in this context.