I'm learning Python. I'm reading some code containing something like this:
class Menu:
'''Display a menu and respond to choices when run.'''
def __init__(self):
self.notebook = Notebook()
self.choices = {
"1": self.show_notes,
"2": self.search_notes,
"3": self.add_note,
"4": self.modify_note,
"5": self.quit
}
def display_menu(self):
print("""
Notebook Menu
1. Show all Notes
2. Search Notes
3. Add Note
4. Modify Note
5. Quit
""")
def run(self):
"""Display the menu and respond to choices."""
while True:
self.display_menu()
choice = input("Enter an option: ")
action = self.choice.get(choice)
if action:
action()
else:
print("{0} is not a valid choice".format(choice))
def show_notes(self):
pass
def search_notes(self):
pass
def add_note(self):
pass
def modify_note(self):
pass
def quit(self):
pass
There are some lines very interesting:
action = self.choice.get(choice)
if action:
action()
Seems it's creating a temporary name for a specific function.
So I did the following test for it to learn more:
>>> def show_notes():
print("show notes")
>>> def search_notes():
print("search notes")
>>> choice = {"1": show_notes, "2": search_notes}
>>> action = choice.get(1)
>>> action()
But I get the following error:
Traceback (most recent call last):
File "<pyshell#64>", line 1, in <module>
action()
TypeError: 'NoneType' object is not callable
Can someone tell me what the technique is and what principle is behind?
Functions are first class objects, and you can create additional references to them. These are just as temporary as you need to them to be, but they can be permanent too.
Your own attempt confused strings and integers however; you used 1 (an integer) where the actual key is '1' (a string). Because you used the wrong key, the dict.get() method returned a default instead, None. None is not a function object and the call fails.
Had you used the right key your code would have worked too:
>>> def show_notes():
... print("show notes")
...
>>> def search_notes():
... print("search notes")
...
>>> choice = {"1": show_notes, "2": search_notes}
>>> choice['1']
<function show_notes at 0x10b1fae18>
>>> choice['1']()
show notes
You can make use of dict.get() returning a default here too, by giving the method a better default to return:
>>> choice.get('none-such-key', search_notes)()
search notes
It seems there's an error in your test. You should be getting "1" and not 1. Getting 1 is returning None because there's nothing defined for key 1. Therefore when you call it like a function it's not valid.
To clarify, "1" is a string and 1 is an integer, which are different keys.
Example:
>>> a = {"1": "yes"}
>>> a.get(1)
>>> a.get("1")
'yes'
Example II (using function):
>>> def hello():
... print "hello"
...
>>> hello()
hello
>>> a = {"1": hello}
>>> b = a.get(1)
>>> b()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
>>> b = a.get("1")
>>> b()
hello
It's not creating a random name for a function. The class method choice is selecting a function and returning it, and it is subsequently being assigned to the variable action. The function is then called by calling action, like you would any function.
Here's an example:
def foo():
print(5)
def getFunction():
return foo
x = getFunction()
x()
The output from this will be 5.
Taking a step back from all of this, you can assign any object to any variable. So consider the following example (I think this will help you understand a little bit more):
def foo():
print(5)
bar = foo
foo = 5
foo()
This will produce an error along the lines of integer objects are not callable. The way this works is that the function object contained in foo is being assigned to variable bar, and the integer 5 is being assigned to foo. The function hasn't changed, but the variable containing it has.
The very first part of defining a function def foo is letting the interpreter know that you are defining a function object and storing in the variable foo. The name and the mechanics of the function are separate.
Does this make sense?
Related
def spring():
return "It is Spring"
def summer():
return "It is Summer"
def autumn():
return "It is Autumn"
def winter():
return "It is Winter"
def default():
return "Invalid Season!"
switch_case = {
1: spring,
2: summer,
3: autumn,
4: winter
}
def switch(x):
return switch_case.get(x, default)()
y= int(input("enter a number for 4 seasons"))
print (switch(y))
I tried removing the () from the end and it doesn't return the default value anymore. When the ()
was placed right after default it didn't return any value when an input between 1 to 4 was made.
Indexing the dict is supposed to return some function, no matter what key is presented. default is returned when the key x doesn't exist. Whatever function does get returned is immediately called.
switch could be defined more verbosely as
def switch(x):
try:
f = switch_case[x]
except KeyError:
f = default
return f()
This code returns the result of the function retrieved by the .get(...) call:
return switch_case.get(x, default)()
# --- equivalent ---
fn = switch_case.get(x, default)
return fn()
This code returns a function, but doesn't call it:
return switch_case.get(x, default)
Using this construction you could return the function and then call it later:
def switch(x):
return switch_case.get(x, default)
y = int(input("enter a number for 4 seasons"))
fn = switch(y)
print(fn()) # call the "switched" function here
Its because you put the function object in the dictionary. Suppose you were using the function directly:
def spring():
return "It is Spring"
spring()
This tells python to load the object in the variable spring and call it. It had better be a callable object, or python will raise an error. It will follow those same steps, regardless of whether the object is really correct. For instance
>>> foo = 1
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
The same thing happened with your dictionary. switch_case.get(x, default) gets the function object from the dictionary, but you still have to call it.
>>> switch_case.get(1, default)
<function spring at 0x7efed8413d90>
>>> switch_case.get(1, default)()
'It is Spring'
Im looking to create something that I can use to check if a string meets a condition like so:
var = "hello"
check = var.checkconditions()
Im just curious as to if its possible as I have never seen it done before.
How would the function/whatever I need to use be set out?
String is a build in class/object and can not be changed. However you can make a personal new class:
class str_class:
def __init__ (self, str):
self.str = str
def checkconditions(self):
# Enter your conditions
var = str_class('hello')
check = var.checkconditions()
Or you could simply make a funtion that takes the string as input and outputs if the condition is met or not:
def checkconditions(str):
# Enter conditions
var = 'Hello'
check = checkconditions(var)
Edit: From other comments it seems as though it is possible but not recommended.
You can use a Class and then use the method check_conditions.
class Check:
def __init__(self):
pass
def check_conditions(string):
#Do whatever you need in here
print(string)
c = Check
c.check_conditions("hello")
This should hopefully do what you need!
You can't directly add the method to the original type.what you can do is subclass the type like
class mystring(str):
def checkconditions(self):
#condition
and then you can instantiate your new class
var = mystring('hello')
var.checkcondition()
but that's still no too practical, if you want to make it more proper you can do this
import __builtin__
__builtin__.str = mystring
var = str("hello")
check = var.checkconditions()
which achieves most of the effect desired.
Unfortunately, objects created by literal syntax will continue to be of the vanilla type and won't have your new methods/attributes.
var = 'hello'
var.checkconditions()
# Output
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'checkconditions'
"""
I have a function (func.py). Structure of which look like this:
database = 'VENUS'
def first_function():
print("do some thing")
def second_function():
print("call third function)
third_function()
def third_function(db = database):
print("do some other thing")
I need to import this function and used the inner defined function. But, I want to use a different key for database. Basically, I want to overwrite database = 'VENUS' and use database = 'MARS' while second function call the third function. is there any way to do this?
Just provide the database name as argument
first_function("MARS")
second_function("MARS")
So the problem here, if I understood correctly, is that the default argument for func.third_function is defined at import time. It doesn't matter if you later modify the func.database variable, since the change will not reflect on the default argument of func.third_function.
One (admittedly hacky) solution is to inject a variable using a closure over the imported function. Example:
file.py:
x = 1
def print_x(xvalue = x)
print(xvalue)
Python console:
>>> import file
>>> file.print_x()
1
>>> file.x = 10
>>> file.print_x() # does not work (as you're probably aware)
1
>>> def inject_var(func_to_inject, var):
def f(*args, **kwargs):
return func_to_inject(var, *args, **kwargs)
return f
>>> file.print_x = inject_var(file.print_x, 10)
>>> file.print_x() # works
10
So using the inject_var as written above, you could probably do:
func.third_function = inject_var(func.third_function, "MARS")
comodin.py
def name():
x = "car"
comodin_1.py
import comodin
print comodin.x
Error:
Traceback (most recent call last):
File "./comodin_2.py", line 4, in <module>
print comodin.x
AttributeError: 'module' object has no attribute 'x'
Is this possible?
In the code you wrote, "x" doesn't exist in "comodin". "x" belongs to the function name() and comodin can't see it.
If you want to access a variable like this, you have to define it at the module scope (not the function scope).
In comodin.py:
x = "car"
def name():
return x
In comodin_1.py:
import comodin
print comodin.name()
print comodin.x
The last 2 lines will print the same thing. The first will execute the name() function and print it's return value, the second just prints the value of x because it's a module variable.
There's a catch: you have to use the 'global' statement if you want to edit the value "x" from a function (add this at the end of comodin.py):
def modify_x_wrong():
x = "nope"
def modify_x():
global x
x = "apple"
And in comodin_1.py:
print comodin.name() # prints "car"
comodin.modify_x_wrong()
print comodin.name() # prints "car", once again
comodin.modify_x()
print comodin.name() # prints "apple"
I want to know if there is a way in python to call the name of an instance variable? For example, if I define a class
>>>class A(object):
... def get_instance_name(self):
... return # The name of the instance variable
>>>obj = A()
>>>obj.get_instance_name()
obj
>>>blah = A()
>>>blah.get_instance_name()
blah
Raise an exception. Not only is it the appropriate way to signal an error, it's also more useful for debugging. The traceback includes the line which did the method call but also additional lines, line numbers, function names, etc. which are more useful for debugging than just a variable name. Example:
class A:
def do(self, x):
if x < 0:
raise ValueError("Negative x")
def wrong(a, x):
a.do(-x)
wrong(A(), 1)
This gives a traceback similar to this, if the exception isn't caught:
Traceback (most recent call last):
File "...", line 1, in <module>
wrong(A(), 1)
File "...", line 7, in wrong
a.do(-x)
File "...", line 4, in do
raise ValueError("Negative x")
ValueError: Negative x
You can also use the traceback module to get this information programmatically, even without an exception (print_stack and friends).
globals() return a dictionary that represents the namespace of the module (the namespace is not this dictionary, this latter only represents it)
class A(object):
def get_instance_name(self):
for name,ob in globals().iteritems():
if ob is self:
return name
obj = A()
print obj.get_instance_name()
blah = A()
print blah.get_instance_name()
tu = (obj,blah)
print [x.get_instance_name() for x in tu]
result
obj
blah
['obj', 'blah']
.
EDIT
Taking account of the remarks, I wrote this new code:
class A(object):
def rondo(self,nameinst,namespace,li,s,seen):
for namea,a in namespace.iteritems():
if a is self:
li.append(nameinst+s+namea)
if namea=='__builtins__':
#this condition prevents the execution to go
# in the following section elif, so that self
# isn't searched among the cascading attributes
# of the builtin objects and the attributes.
# This is to avoid to explore all the big tree
# of builtin objects and their cascading attributes.
# It supposes that every builtin object has not
# received the instance, of which the names are
# searched, as a new attribute. This makes sense.
for bn,b in __builtins__.__dict__.iteritems():
if b is self:
li.append(nameinst+'-'+b)
elif hasattr(a,'__dict__') \
and not any(n+s+namea in seen for n in seen)\
and not any(n+s+namea in li for n in li):
seen.append(nameinst+s+namea)
self.rondo(nameinst+s+namea,a.__dict__,li,'.')
else:
seen.append(nameinst+s+namea)
def get_instance_name(self):
li = []
seen = []
self.rondo('',globals(),li,'')
return li if li else None
With the following
bumbum = A()
blah = A()
print "bumbum's names:\n",bumbum.get_instance_name()
print "\nmap(lambda y:y.get_instance_name(), (bumbum,blah) :\n",map(lambda y:y.get_instance_name(), (bumbum,blah))
print "\n[y.get_instance_name() for y in (bumbum,blah)] :\n",[y.get_instance_name() for y in (bumbum,blah)]
the result is
bumbum's names:
['bumbum']
map(lambda y:y.get_instance_name(), (bumbum,blah) :
[['bumbum'], ['blah']]
[y.get_instance_name() for y in (bumbum,blah)] :
[['bumbum', 'y'], ['blah', 'y']]
The second list comprehension shows that the function get_instance_name() must be used with care. In the list comp, identifier y is assigned in turn to every element of (bumbum,blah) then the finction finds it out as a name of the instance !
.
Now, a more complex situation:
ahah = A() # ahah : first name for this instance
class B(object):
pass
bobo = B()
bobo.x = ahah # bobo.x : second name for ahah
jupiter = bobo.x # jupiter : third name for ahah
class C(object):
def __init__(self):
self.azerty = jupiter # fourth name for ahah
ccc = C()
kkk = ccc.azerty # kkk : fifth name for ahah
bobo.x.inxnum = 1005
bobo.x.inxwhat = kkk # bobo.x.inxwhat : fifth name for ahah
# Since bobo.x is instance ahah, this instruction also
# creates attribute inxwhat in ahah instance's __dict__ .
# Consequently, instance ahah having already 5 names,
# this instruction adds 5 additional names, each one
# ending with .inxwhat
# By the way, this kkk being ahah itself, it results that ahah
# is the value of its own attribute inxwhat.
print ahah.get_instance_name()
result
['bobo.x', 'bobo.x.inxwhat',
'ahah', 'ahah.inxwhat',
'jupiter', 'jupiter.inxwhat',
'kkk', 'kkk.inxwhat',
'ccc.azerty', 'ccc.azerty.inxwhat']
I concur to judge this solution a little heavy and that if a coder thinks he needs such a heavy function, it is probably because the algorithm isn't optimal. But I find interesting to see that it's possible to do this in Python though it doesn't seem evident.
I say heavy, not hacky, I don't find it's hacky, by the way.
No, you can't. Objects can have any number of names, so the question doesn't even make sense. Consider:
a1 = a2 = a3 = A()
What is the name of the instance of A()?