Python - Accessing object variables within a module - python

I have a module (a python file, that is) containing different classes with functions, here called 'Experiment_1'. When running it as the main file, all objects can access all variables - object_2 functions can access variables in object_1, and so on:
# Experiment_1
class A():
def process1(self):
self.x=10
def process2(self):
self.y=20
class B():
def addition(self):
summed=object_1.x+object_1.y
print(summed)
if __name__ == '__main__':
object_1=A()
object_2=B()
object_1.process1()
object_1.process2()
object_2.addition()
Next, I attempt to run this in file 'Experiment_2' as an imported module:
# Experiment_2
from Experiment_1 import *
import Experiment_1
object_1=A()
object_2=B()
object_1.process1()
object_1.process2()
object_2.addition()
And get the error message:
File "C:\Program Files\Sublime Text 3\Experiment_2.py", line 10, in <module>
object_2.addition()
File "C:\Program Files\Sublime Text 3\Experiment_1.py", line 10, in addition
summed=object_1.x+object_1.y
NameError: name 'object_1' is not defined
Thus, object_2 can no longer access the variables of object_1. I have been searching a lot to find a solution to this but may be using the wrong keywords or simply lack the understanding to recognize the answer when I see it - can anyone give me a hint how to proceed?
Thanks in advance!

When you import a module, the block starting with if __name__ == "__main__": does not get executed as the name variable of the module is set to the name you imported it ("Experiment_1" in this case). From official documentation:
When a Python module or package is imported, name is set to the module’s name. Usually, this is the name of the Python file itself without the .py extension
If you'd like your code to get executed regardless of it is imported or run directly from command line, just place it at the bottom.

The issue is your object instantiation in Experiment_1.py. The code inside the below block only get executed if you run the file as a script and NOT when you import it.
if __name__ == '__main__':
pass
So in your object of class B, the below statement in addition method,
#Experiment_1.py
summed=object_1.x+object_1.y
see object_1 as not defined, because it is only instantiated in the if __name__ == '__main__': block which is not executed when importing the module.
See this question: What does if __name__ == "__main__": do?
And for the solution, If you wish to use the module structure, the work around is to pass the object to the addition method.
#Experiment_1.py
class A():
def process1(self):
self.x = 10
def process2(self):
self.y = 20
class B():
def addition(self, obj):
summed = obj.x + obj.y
print(summed)
then in your next file,
from Experiment1 import *
# import Experiment1
object_1=A()
object_2=B()
object_1.process1()
object_1.process2()
object_2.addition(object_1)

Not tested, but I think the following script is the solution, because you need to inherit attributes. Test changes (on first module) and give a response, please.
class A():
def __init__(self):
self.x = 0
self.y = 0
def process1(self):
self.x=10
def process2(self):
self.y=20
class B(A):
def __init__(self):
A.__init__(self)
def addition(self):
summed = self.x + self.y
print(summed)
if __name__ == '__main__':
object_1 = A()
object_2 = B()
object_1.process1()
object_1.process2()
object_2.addition()
Edit
Sorry, I don't realize that B can do all purpose. This is the good solution:
experiment1.py
class A:
def __init__(self):
self.x = 0
self.y = 0
def process1(self):
self.x = 10
def process2(self):
self.y = 20
class B(A):
def __init__(self):
A.__init__(self)
def addition(self):
summed = self.x + self.y
print(summed)
if __name__ == '__main__':
object_1 = B()
object_1.process1()
object_1.process2()
object_1.addition()
experiment2.py
from Experiment1 import *
if __name__ == '__main__':
object_1 = B()
object_1.process1()
object_2.process2()
object_2.addition()
Explaination: because B class inherits methods and attributes of A class, you don't need instantiate A class. All the work can be run by B class.
As other user said, in B class you are using global variable declared in main section that is unreachable when you import module, this is the principle of the if __name__ == '__main__':

Related

Magic mock assert_called_once vs assert_called_once_with weird behaviour

I am noticing a weird behavior with assert_called_once and assert_called_once_with in python. This is my real simple test:
File module/a.py
from .b import B
class A(object):
def __init__(self):
self.b = B("hi")
def call_b_hello(self):
print(self.b.hello())
File module/b.py
class B(object):
def __init__(self, string):
print("created B")
self.string = string;
def hello(self):
return self.string
These are my tests:
import unittest
from mock import patch
from module.a import A
class MCVETests(unittest.TestCase):
#patch('module.a.B')
def testAcallBwithMockPassCorrect(self, b1):
a = A()
b1.assert_called_once_with("hi")
a.call_b_hello()
a.b.hello.assert_called_once()
#patch('module.a.B')
def testAcallBwithMockPassCorrectWith(self, b1):
a = A()
b1.assert_called_once_with("hi")
a.call_b_hello()
a.b.hello.assert_called_once_with()
#patch('module.a.B')
def testAcallBwithMockFailCorrectWith(self, b1):
a = A()
b1.assert_called_once_with("hi")
a.b.hello.assert_called_once_with()
#patch('module.a.B')
def testAcallBwithMockPassWrong(self, b1):
a = A()
b1.assert_called_once_with("hi")
a.b.hello.assert_called_once()
if __name__ == '__main__':
unittest.main()
My problem as stated in the name of the function is:
Test 1 passes correctly
Test 2 passes correctly
Test 3 fails correctly (I've removed the call to b)
Test 4 passes I am not sure why.
Am I doing something wrong? I am not sure but reading the documentation docs python:
assert_called_once(*args, **kwargs)
Assert that the mock was called exactly once.
This is old, but for others landing here...
For python < 3.6, assert_called_once isn't a thing and so you're actually making a mocked function call which doesn't error
Please see: http://engineroom.trackmaven.com/blog/mocking-mistakes/
You can check the call count instead.

Python: isinstance() undefined global name

I'm new with Python and I'm trying to use classes to program using objects as I do with C++.
I wrote 3 .py files.
a.py
from b import *
class A:
def __init__(self):
self.m_tName = "A"
def test(self):
tB = B()
tB.do( self )
b.py
from a import *
class B:
def __init__(self):
self.m_tName = "B"
def do(self, tA ):
if not isinstance( tA, A ):
print ( "invalid parameter" )
print( "OK" )
demo.py:
from a import *
if __name__ == "__main__":
tA = A()
tA.test()
As you can see I want to use a A() object to call the member function test() that creates a B() object and call the member function do() that uses a A() object.
So in B::do() I want to check the parameters using the built-in function isinstance(). But python tells me that there's a NameError: global name 'A' is not defined.
The A() class file is imported at the top of the b.py file.
Does anyone know what I'm doing wrong here ?
As pointed in some comment, circular dependencies are not well handled if imported in the form from a import A.
In short, the problem with ... import * is that is causes the local scope to have all its declarations overridden, in effect making the identification of from which module (in your case) a class comes from. This causes exactly what you are facing.
Changing the import statement in the following way, together with a classified reference to a.A, produces OK as output.
import a
class B:
def __init__(self):
self.m_tName = "B"
def do(self, tA ):
print tA
if not isinstance( tA, a.A ):
print ( "invalid parameter" )
print( "OK" )
As a bit of additional information, this has already been discussed in Why is "import *" bad?. I would point in special to this answer: https://stackoverflow.com/a/2454460/1540197.
**Edit:**This article explain the import confusion.
You have a circular dependancy, a.py and b.py import each other.
You could move either import statement inside the method where it is used.
So b.py would become:
class A:
def __init__(self):
self.m_tName = "A"
def test(self):
from b import B
tB = B()
tB.do( self )

NameError: global name 'myExample2' is not defined # modules

Here is my example.py file:
from myimport import *
def main():
myimport2 = myimport(10)
myimport2.myExample()
if __name__ == "__main__":
main()
And here is myimport.py file:
class myClass:
def __init__(self, number):
self.number = number
def myExample(self):
result = myExample2(self.number) - self.number
print(result)
def myExample2(num):
return num*num
When I run example.py file, i have the following error:
NameError: global name 'myExample2' is not defined
How can I fix that?
Here's a simple fix to your code.
from myimport import myClass #import the class you needed
def main():
myClassInstance = myClass(10) #Create an instance of that class
myClassInstance.myExample()
if __name__ == "__main__":
main()
And the myimport.py:
class myClass:
def __init__(self, number):
self.number = number
def myExample(self):
result = self.myExample2(self.number) - self.number
print(result)
def myExample2(self, num): #the instance object is always needed
#as the first argument in a class method
return num*num
I see two errors in you code:
You need to call myExample2 as self.myExample2(...)
You need to add self when defining myExample2: def myExample2(self, num): ...
First, I agree in with alKid's answer. This is really more a comment on the question than an answer, but I don't have the reputation to comment.
My comment:
The global name that causes the error is myImport not myExample2
Explanation:
The full error message generated by my Python 2.7 is:
Message File Name Line Position
Traceback
<module> C:\xxx\example.py 7
main C:\xxx\example.py 3
NameError: global name 'myimport' is not defined
I found this question when I was trying to track down an obscure "global name not defined" error in my own code. Because the error message in the question is incorrect, I ended up more confused. When I actually ran the code and saw the actual error, it all made sense.
I hope this prevents anyone finding this thread from having the same problem I did. If someone with more reputation than I wants to turn this into a comment or fix the question, please feel free.
You have to create an instance of the myClass class, and not the instance of the whole module(and i edit variables names to be less awful):
from myimport import *
def main():
#myobj = myimport.myClass(10)
# the next string is similar to above, you can do both ways
myobj = myClass(10)
myobj.myExample()
if __name__ == "__main__":
main()
While the other answers are correct, I wonder if there is really a need for myExample2() being a method. You could as well implement it standalone:
def myExample2(num):
return num*num
class myClass:
def __init__(self, number):
self.number = number
def myExample(self):
result = myExample2(self.number) - self.number
print(result)
Or, if you want to keep your namespace clean, implement it as a method, but as it doesn't need self, as a #staticmethod:
def myExample2(num):
return num*num
class myClass:
def __init__(self, number):
self.number = number
def myExample(self):
result = self.myExample2(self.number) - self.number
print(result)
#staticmethod
def myExample2(num):
return num*num

Access subclass variables from parent file

I am looking for a way to access a subclasses variables from the parent class which is instantiated in a different file. For example
basefile.py:
class A(object): #gets subclassed
var = 0 #place holder
def printTheVar(self):
print self.var
class B(object):
def buildAndCallA(self):
a = A()
a.printTheVar()
implementationfile.py:
from basefile import *
class A(A):
var = 10
if __name__ == '__main__':
b = B()
b.buildAndCallA()
When I run:
$ python implementationfile.py
I get 0. I want to get 10
When both parent class and implementation class are in the same file, this is obviously not a problem but I have a project structure which requires they not be:
somedir/
| basefile.py
| implementations/
| -- implementationA.py
| -- implementationB.py
| -- implementationC.py
I think that the abc module might help but my experiments with that have proven fruitless so far.
I'd suggest, if possible, you pass the class you want to use to the buildAndCallA method. So it should look something like this:
def buildAndCallA(self,cls):
a = cls()
a.printTheVar()
And then you can call it like this:
b.buildAndCallA(A)
Then it will use whatever version of the A class is in scope at the time it is called.
You could even set it up with a default parameter, so it will use the version of A in the base file by default, but you can still override it when necessary.
def buildAndCallA(self,cls=A):
a = cls()
a.printTheVar()
Then if you call b.buildAndCallA() with no parameter, it will construct an instance of the A class from the base file.
#James's answer got me most of the ways there. Here is a more global way to do it using three files for clarity (which is really how the project is organized anyways)
script.py:
if __name__ == '__main__':
if sys.argv[0] == 'useImplementation1'
import implementations.implementation1 as implementation
elif sys.argv[1] == 'useImplementation2':
import implementations.implementation2 as implementation
b = implementation.B(cls=implementation)
b.buildAndCallA()
basefile.py (notice the A = cls.A this is the key):
class A(object):
var = 0 #place holder
def printTheVar(self):
print self.var
class B(object):
def __init__(self,cls):
global A
A = cls.A
def buildAndCallA(self):
a = A()
a.printTheVar()
implementation1.py:
from basefile import *
class A(A):
var = 10

class method with no arguments produces TypeError

This code:
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
def test():
print('test')
if __name__ == '__main__':
x = testclass(2,3)
yields:
Error:
TypeError:test() takes no argument(1 given)
I'm calling the test function without any parameter, why does the error say that I have given one?
You call the methods as self.test(). You should mentally translate that to test(self) to find out how the call will be "received" in the function's definition. Your definition of test however is simply def test(), which has no place for the self to go, so you get the error you observed.
Why is this the case? Because Python can only look up attributes when specifically given an object to look in (and looking up attributes includes method calls). So in order for the method to do anything that depends on which object it was invoked on, it needs to receive that object somehow. The mechanism for receiving it is for it to be the first argument.
It is possible to tell Python that test doesn't actually need self at all, using the staticmethod decorator. In that case Python knows the method doesn't need self, so it doesn't try to add it in as the first argument. So either of the following definitions for test will fix your problem:
def test(self):
print('test')
OR:
#staticmethod
def test():
print('test')
Note that this is only to do with methods invoked on objects (which always looks like some_object.some_method(...)). Normal function invocation (looking like function(...)) has nothing "left of the dot", so there is no self, so it won't be automatically passed.
Pass self to your test method:
def test(self):
print('test')
You need to do this because Python explicitly passes a parameter referring to the instantiated object as the first parameter. It shouldn't be omitted, even if there are no arguments to the method (because of the error specified).
Python always passes the instance as the first argument of instance methods, this means that sometimes the error messages concerning the number of arguments seems to be off by one.
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
def test(self): ## instance method
print('test', self)
if __name__ == '__main__':
x = testclass(2,3)
If you don't need access to the class or the instance, you can use a staticmethod as shown below
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
#staticmethod
def test():
print('test')
if __name__ == '__main__':
x = testclass(2,3)
A classmethod is similar, if you need access to the class, but not the instance
class testclass:
def __init__(self,x,y):
self.x = x
self.y = y
self.test()
#classmethod
def test(cls):
print('test', cls)
if __name__ == '__main__':
x = testclass(2,3)

Categories