I have two scripts. The first containing a class, with class variables defined and a function using those class variables. The second script calls the class and function within a function of it's own.
This sort of set up works fine for functions inside a class, however adding class variables is causing me the below error. Can anyone explain why, please and what I need to do to fix?
Thanks
obj1.py:
class my_test_class():
def __init__(self):
self.test1 = 'test1'
self.test2 = 'test2'
self.test3 = 'test3'
def test_func(self, var):
new_var = print(var, self.test1, self.test2, self.test3)
obj2.py
from obj1 import *
def import_test():
target_var = my_test_class.test_func('my test is:')
print(target_var)
import_test()
Error:
Traceback (most recent call last):
File "G:/Python27/Test/obj2.py", line 9, in <module>
import_test()
File "G:/Python27/Test/obj2.py", line 6, in import_test
target_var = my_test_class.test_func('my test is:')
TypeError: test_func() missing 1 required positional argument: 'var'
As the commentors have pointed out, since the test_func is a class method, we need to call it using a class instance object.
Also print function returns None, so doing new_var = print(var, self.test1, self.test2, self.test3) assigns new_var=None, so if you want to return the variable, you need to assign new_var = ' '.join([var, self.test1, self.test2, self.test3]), which creates a string with a whitespace between all the words, and return new_var
Combining all of this, the code comes out as follows
class my_test_class():
def __init__(self):
self.test1 = 'test1'
self.test2 = 'test2'
self.test3 = 'test3'
def test_func(self, var):
#Assign value to new_var and return it
new_var = ' '.join([var, self.test1, self.test2, self.test3])
return new_var
def import_test():
#Create instance of my_test_class
test_class = my_test_class()
#Call test_func using instance of my_test_class
print(test_class.test_func('my test is:'))
import_test()
The output will be my test is: test1 test2 test3
Related
After I ran the code below, I got NameError: name 'result' is not defined. I tried to use class variable in a class method. Why does it give me an error?
class Test():
def __init__(self):
self.a=self.test1()
self.result = Test.test2()+Test.test3()
def test1(self):
a=100
return a
result = Test.test3()+100
#classmethod
def test2(cls):
b=200
return b
#staticmethod
def test3():
print("Testing3 is calling ")
c=500+Test.result
return c
Error:
result = Test.test3()+100
File "<ipython-input-29-29d4025016c1>", line 18, in test3
c=500+result
NameError: name 'result' is not defined
At the time the line of code in question is evaluated, result is not a class variable. It's been defined as an instance variable here:
def __init__(self):
self.a=self.test1()
self.result = Test.test2()+Test.test3()
but the line of code that defines Test.result as a class variable:
result = Test.test3()+100
has not yet finished executing at the time that it calls test3(), which itself has a dependency on Test.result.
Instead of using a global variable, I'm trying to make an instance of a variable in a class, as it seems to be best practice. How do I reference this variable across other functions in the class? I would have thought that Test.running_sum would work or at least running_sum in test_function, but I'm not having any luck with either. Thanks very much!
class Test:
def __init__(self):
self.root = None
running_sum = 0
def test_function(self):
print(Test.running_sum)
return
x = Test()
x.test_function()
Error:
Traceback (most recent call last):
File "so.py", line 1, in <module>
class Test:
File "so.py", line 10, in Test
x = Test()
NameError: name 'Test' is not defined
Use self parameter provided in the method signature.
Note that what you wrote is not a method, but an external function using class Test. To write a method of Test, the def should be at one level of indentation inside class Test as following:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
There are several things to add if you want an explanation behind this "best practice".
Assuming you write the following code:
class Test:
numbers = []
def add(self, value):
self.numbers.append(value)
The Test.numbers list is instantiated once and shared accross all instances of Test. Therefore, if 2 different instances add to the list, both act on the same list:
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a.numbers == b.numbers == Test.numbers
When creating instance variables in the __init__ function, __init__ will be run at each instantiation, and therefore, the list will no longer be shared because they will be created for each individual instances.
class Test:
def __init__(self):
self.numbers = []
def add(self, number):
self.numbers.append(number)
a = Test()
b = Test()
a.add(5)
b.add(10)
assert a != b
As an object attribute: each object gets its own.
Test is the class; self is the Test object that invoked the method.
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
self.running_sum += 1
print(self.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
1
As a class attribute: all objects share the same variable.
self.__class__ is the class of the invoking object (i.e. Test).
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
self.__class__.running_sum += 1
print(self.__class__.running_sum)
return
x = Test()
y = Test()
x.test_function()
y.test_function()
Output:
1
2
how do I reference this variable across other functions in the class
Several things I see wrong here. First of all, you are calling running_sum on the class itself which doesn't make sense since you are declaring running_sum as an attribute of an instance of Test. Second, from the way you formatted your question, it seems that test_function is outside of the class Test which doesn't make sense since you are passing self to it, implying it is an instance method. To resolve you could do this:
class Test:
def __init__(self):
self.running_sum = 0
def test_function(self):
print(self.running_sum)
Then again this also is weird... Why would you need a "test_function" when you can simply test the value of running_sum by simply doing:
x = Test()
x.running_sum
In your __init__ function, you've created a local variable. That variable will no longer exist after the function has completed.
If you want to create a variable specific to the object x then you should create a self.running_sum variable
class Test:
def __init__(self):
self.root = None
self.running_sum = 0
def test_function(self):
print(self.running_sum)
If you want to create a variable specific to the class Test then you should create a Test.running_sum variable.
class Test:
running_sum = 0
def __init__(self):
self.root = None
def test_function(self):
print(Test.running_sum)
I have an Class which creates objects that contain a dict of functions. These functions are defined in the class.
class Foo:
def __init__(self, name, functions):
self.name = name
self.functions = functions
def myFunction1(self):
print self.name
return 'foo'
def myFunction2(self):
return 'bar'
In another file, I instance an object of the previous class and I want to run one of the functions that is in the dict.
from foo import Foo
myFunctions = Foo.('a name', {0 : Foo.myFunction1, 1 : Foo.myFunction2})
myFunctions.functions[0]()
I have this error :
TypeError: unbound method myFunction1() must be called with Foo instance as first argument.
I understand that when I execute the last line, I just call the function without call it with an object of Foo as first argument. But I can not write this :
myFunctions.myFunctions.functions[0]()
How can I call a function of this class which is stored in a dict attribute ?
I do not really understand what you are trying to achieve with this functions dictionary, but note that instance.function() is equivalent to Class.function(instance). In the former, instance is implicitly passed as the self parameter; in the latter, you pass it explicitly.
Thus, you could try the following.
foo = Foo({0 : Foo.myFunction1, 1 : Foo.myFunction2})
print foo.functions[0](foo)
If you need an object method, best way is to write another function that will call the correct function
class Foo:
def __init__(self, functions):
self.functions = functions
def myFunction1(self):
return 'foo'
def myFunction2(self):
return 'bar'
def run_func(self, func_key, *args, **kwargs):
func_name = self.functions.get(func_key)
if func_name and hasattr(self, func_name):
return getattr(self, func_name)(*args, **kwargs)
else:
return None
fun_dict = {0:'myFunction1', 1:'myFunction2'}
a = Foo(fun_dict)
print a.run_func(0)
You can send even arguments in this way.
def myFunction1(self, name):
return name
print a.run_func(0, "test")
This will print test by the myFunction1
You can call functions as a dictionary like this:
tmp = Foo()
tmp.functions={0:tmp.myFunction1, 1:tmp.myFunction2}
myFunctions = tmp.functions
print myFunctions.functions[0]()
Here is the simplest way to switch between functions:
dict = {0 : Foo.myFunction1(), 1 : Foo.myFunction2()}
function = fn_dict.get(0)
function()
I'm trying to define a method which takes a python list as one of its input parameters. By contrast regular functions have no problem accepting lists as input parameters. How Come?
# Simple function that works
def func(param1, param2):
for item in param1:
print item+" "+param2
var1 = ['sjd', 'jkfgljf', 'poipopo', 'uyuyuyu']
var2 = 'is nonsense'
func(var1, var2)
# Simple function produces the following output:
# sjd is nonsense
# jkfgljf is nonsense
# poipopo is nonsense
# uyuyuyu is nonsense
If I try to get a similar effect with a method inside a class like this:
# Simple class
class test():
def __init__(self):
pass
def test_method(par1, par2):
for itm in par1:
print itm+" "+par2
# This executes with no error
obj = test()
# This fails
obj.test_method(var1, var2)
# Error message will be:
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: test_method() takes exactly 2 arguments (3 given)
Seems like I'm missing something very basic, any help will be greatly appreciated.
If you want test_method to have access to data members in your class, then you need to pass self, as in:
def test_method(self, par1, par2):
If test_method does not need to have access to data members in your class, then declare it as a static method:
#staticmethod
def test_method(par1, par2):
For reference, let's say you have a class that holds a number and you want to return said number in a method, and you have another method that gives the product of two numbers, but doesn't depend on anything in your class. Here's how you would do it:
class myClass(object):
def __init__(self, num):
self.number = num
def getNum(self):
return self.number
#staticmethod
def product(num1,num2):
return num1*num2
if __name__ == "__main__":
obj = myClass(4)
print obj.getNum()
print myClass.product(2,3)
Prints:
4
6
Just change:
def test_method(par1, par2):
to
def test_method(self, par1, par2):
I've written a program in which I have a fairly typical class. In this class I create multiple namedtuple objects. The namedtuple objects hold many items, which all work fine, except for lambda functions that I try to bind to it. Below is a stripped down example and the error message that I am receiving. Hope someone knows why this is going wrong. Thanks in advance!
FILE: test.py
from equations import *
from collections import namedtuple
class Test:
def __init__(self, nr):
self.obj = self.create(nr)
print self.obj.name
print self.obj.f1(2)
def create(self, nr):
obj = namedtuple("struct", "name f1 f2")
obj.name = str(nr)
(obj.f1, obj.f2) = get_func(nr)
return obj
test = Test(1)
FILE: equations.py
def get_func(nr):
return (lambda x: test1(x), lambda x: test2(x))
def test1(x):
return (x/1)
def test2(x):
return (x/2)
ERROR:
Traceback (most recent call last):
File "test.py", line 17, in <module>
test = Test(1)
File "test.py", line 8, in __init__
print self.obj.f1(2)
TypeError: unbound method <lambda>() must be called with struct instance as first argument (got int instance instead)`
The namedtuple() constructor returns a class, not an instance itself. You are adding methods to that class. As such, your lambda's must accept a self argument.
In any case, you should create instances of the named tuple class you created. If you don't want to give your lambdas a self first argument, adding them to the instance you then created would work fine:
from equations import *
from collections import namedtuple
Struct = namedtuple("struct", "name f1 f2")
class Test:
def __init__(self, nr):
self.obj = self.create(nr)
print self.obj.name
print self.obj.f1(2)
def create(self, nr):
obj = Struct(str(nr), *get_func(nr))
return obj
test = Test(1)