I'm relatively new to python and I have a class that has a bunch of different function. I read in the user input and depending on the user input I call a different function. Instead of having a bunch of if else statements I thought it would be better to have a dictionary of functions so currently my class looks like this:
class Foo:
def func1(self):
#do something
def func2(self, arg1):
#do something else
def func3(self, arg1, arg2):
#do something
def func4(self, arg1):
#do something
def __init__(self):
self.functions = {"FUNC2": func2, "FUNC4":func4}
def run_loop(self):
while 1:
user_input = raw_input()
cmd = user_input.split(' ')[0]
if cmd in self.functions:
self.functions[cmd].__get__(self, type(self))()
else:
#call other functions
I'm calling this in a main.py like so:
c = Class()
c.run_loop()
My issue is that I'm getting the following error NameError: global name 'func2' is not defined`. I'm not really sure why this is happening. I get the error in the constructor. Any ideas?
You need to specify that the function is within the class by adding self before it.
def __init__(self):
self.functions = {"FUNC2": self.func2, "FUNC4":self.func4}
You need to use self to access class functions from other function in same class. The corrected code will be
self.functions = {"FUNC2": self.func2, "FUNC4":self.func4}
The function is not identified within the class. Function "func2" & "func4" are part of the class and can be referred using the self...
Related
Can I actually use functions of another class as a parameter and attribute for a class instance/object?
I noticed if I do something like this there are lots of oddities (Note that I use Jupyter lab):
class ObjectClass:
#A class; I will insert a function into generalMethod
def __init__(self, generalMethod):
self.generalMethod = generalMethod
class GeneralMethods():
#Two different methods that I want to call
def method1(self):
add(2)
def method2(self):
print("Hey now, you're an all-star, get your game on, go play" )
return "Hey, I can return stuff, at least!",2
def add(input):
#A simple function that adds 1 to input
print(1 + input)
#Creating two objects with different methods as inputs from GeneralMethods
gm = GeneralMethods()
object1 = ObjectClass(gm.method1())
object2 = ObjectClass(gm.method2())
#Attempting to call anything from generalMethod; a getter method does the same
object1.generalMethod
object2.generalMethod
gm.method1() and gm.method2() does everything inside it, even when I simply declare it as a parameter of the object/instance!
But anyObject.generalMethod doesn't do anything besides return whatever is in the return when I call it, and if a function is in there, it will return None.
So can I actually call a function from the attribute and it perform like gm.method1() does when calling it from the attribute (anyObjectIChoose.generalMethod).
You can pass a function as parameter:
def foo():
print('hello')
def bar(_f):
_f()
bar(_f=foo)
'hello'
Note that when you add () to function's name, you invoke it. To pass as param you need just the name, not invoke it.
How can I execute code using a class with a "with" statement, but don't execute code until certain function is run?
For example if I have the class:
class Foo:
def __init__(self):
self.bar = "baz"
def __enter__(self):
return self
def __exit__(self, x, y, z):
return None
def run(self):
# Start running code inserted into __enter__?
And I used the class like:
bar = Foo()
with bar as f:
print(f.bar)
# Don't execute this code until run() is called
bar.run()
# Now execute the code that was inserted into the with statement above
Is this possible?
I don't think you can do that, the way your code is currently written. However, you effectively want bar.run() to be called from Foo.__enter__.
class Foo:
def __init__(self, pre=None):
self.bar = "baz"
def __enter__(self):
self.run()
return `self`
def __exit__(self, x, y, z):
return None
def run(self):
# Start running code inserted into __enter__?
bar = Foo()
with bar as f:
print(f.bar)
Nope, the code inside the with block gets executed only during the execution of the with block. It's not, for example, passed as a closure to the context manager.
If you want that behaviour, you could possibly use a decorator and a local function declaration instead of a with block.
class Foo:
def __init__(self, callback):
self.callback = callback
self.bar = 'baz'
def run(self):
# Setup goes here.
try:
self.callback(self)
finally:
# Cleanup goes here.
pass
#Foo
def bar(f):
print(f.bar)
bar.run()
That's as close to your example as I can get, names included. I think perhaps applying the constructor directly as a decorator is a bit ungainly - you might prefer to have a separate decorator function to call and return a class instance, but you get the general idea.
The current code I have, allows the function to call the wrapper decorator, and uses the function name in its code. However, I'm looking for a way to give the function a 'alias' in a way as an argument. Here's the current code:
import os, sys
# Take user input
message = input('type command: ')
# Command wrapper
ALLCOMMANDS = {}
def command(function):
ALLCOMMANDS[function.__name__] = function
return function
# Commands
#command
def foo():
print("bar")
#command
def goo():
print('ber')
# Run appropriate command
if message in ALLCOMMANDS:
ALLCOMMANDS[message]()
For example I would want to be able to call the function by a name such as '!foo' from the user input, so maybe the argument would look like #command(name='!foo'), I just don't know where to go from there to use that argument in the decorator since it already has an argument.
I attempted
# Command wrapper
ALLCOMMANDS = {}
def command(name):
ALLCOMMANDS[name] = name
return name
but keep getting errors and I assume I am missing something
You should read up a bit more on python decorators. You're getting an error with:
def command(name):
ALLCOMMANDS[name] = name
return name
Because of the return name.
Decorators are just syntactic sugar. This:
#command
def foo():
print('bar')
Is equivalent to:
def foo():
print('bar')
foo = command(foo)
From this you can see why your original decorator works. At the end you return function.
Things get a little tricker when you have a decorator that takes arguments. Desugared the following:
#command('nickname')
def foo():
print('bar')
Looks like this:
def foo():
print('bar')
foo = command('nickname')(foo)
So, to write a decorator that takes arguments, the decorator needs to return a function that takes the function to decorate as an argument:
def command(nickname):
def wrapped(f):
ALLCOMMANDS[nickname] = f
return f
return wrapped
Also consider making ALLCOMMANDS an attribute on your command instead of a global (UPPER_SNAKE is usually reserved for constants):
def command(nickname):
def wrapped(f):
command._functions[nickname] = f
return f
return wrapped
command._functions = {}
I am trying to access to another variable that inside a function and also that is from another class, so I coded it in that way
class Helloworld:
def printHello(self):
self.hello = 'Hello World'
print (self.hello)
class Helloworld2(Helloworld):
def printHello2(self)
print(self.hello)
b = Helloworld2()
b.printHello2()
a = Helloworld()
a.printHello()
However, this gives me that error: AttributeError: 'Helloworld2' object has no attribute 'hello'. So, what would be the simplest way to access to that variable?
That's because you never call printHello(self) that declare your self.hello.
To make it work you need to do:
class Helloworld2(Helloworld):
def printHello2(self):
super().printHello()
print(self.hello)
Or move declaration of you self.hello to __init__() which would be more preferred way.
You should initialise the instance of the class via the __init__() function, this means that when it is created, these values are set.
That would make your code look like:
class Helloworld:
def __init__(self):
#sets self.hello on creation of object
self.hello = 'Hello World'
def printHello(self):
print (self.hello)
class Helloworld2(Helloworld):
def printHello2(self):
print(self.hello)
b = Helloworld2()
b.printHello2()
a = Helloworld()
a.printHello()
An alternative, with your current code is to just call printHello(), either at the top level, with b.printHello(), or within printHello2. Note that in this case, you don't actually need to use super().printHello() as you are not re-defining that function in Helloworld2, though it would be required if you did.
In a Javascript object when I would want to initiate several functions inside an object, say myObject, I would have an init function that would call those methods to me initialized and I would simple call myObject.init(). How would I do this in python? Would the following be ok?
class Test(object):
def __init__(self, arg):
self.arg = arg
def init(self):
self.some_function()
self.some_other_function()
def some_function(self):
pass
def some_other_function(self):
pass
my_test = Test("test")
my_test.init()
Thanks for reading!
Yes. That should work fine. but I would give some other name than init(), as it would be explicit and different from default __init__