I am completely new to the python class concept. After searching for a solution for some days, I hope I will get help here:
I want a python class where I import a function and use it there. The main code should be able to call the function from the class. for that I have two files in the same folder.
Thanks to #cdarke, #DeepSpace and #MosesKoledoye, I edited the mistake, but sadly that wasn't it.
I still get the Error:
test 0
Traceback (most recent call last):
File "run.py", line 3, in <module>
foo.doit()
File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 8, in doit
self.timer(5)
File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 6, in timer
zeit.sleep(2)
NameError: global name 'zeit' is not defined
#wombatz got the right tip:
it must be self.zeit.sleep(2) or Test.zeit.sleep(2). the import could be also done above the class declaration.
Test.Py
class Test:
import time as zeit
def timer(self, count):
for i in range(count):
print("test "+str(i))
self.zeit.sleep(2) <-- self is importent, otherwise, move the import above the class declaration
def doit(self):
self.timer(5)
and
run.py
from test import Test
foo = Test()
foo.doit()
when I try to python run.py I get this error:
test 0
Traceback (most recent call last):
File "run.py", line 3, in <module>
foo.doit()
File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 8, in doit
self.timer(5)
File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 6, in timer
sleep(2)
NameError: global name 'sleep' is not defined
What I understand from the error is that the import in the class is not recognized. But how can I achive that the import in the class is recognized?
Everything defined inside the namespace of a class has to be accessed from that class. That holds for methods, variables, nested classes and everything else including modules.
If you really want to import a module inside a class you must access it from that class:
class Test:
import time as zeit
def timer(self):
self.zeit.sleep(2)
# or Test.zeit.sleep(2)
But why would you import the module inside the class anyway? I can't think of a use case for that despite from wanting it to put into that namespace.
You really should move the import to the top of the module. Then you can call zeit.sleep(2) inside the class without prefixing self or Test.
Also you should not use non-english identifiers like zeit. People who only speak english should be able to read your code.
sleep is not a python builtin, and the name as is, does not reference any object. So Python has rightly raised a NameEror.
You intend to:
import time as zeit
zeit.sleep(2)
And move import time as zeit to the top of the module.
The time module aliased as zeit is probably not appearing in your module's global symbol table because it was imported inside a class.
You want time.sleep. You can also use;
from time import sleep
Edit: Importing within class scope issues explained here.
You're almost there! sleep is a function within the time module. This means that the name sleep doesn't exist unless its understood within the context of time, unless you define it on your own. Since you didn't define it on your own, you can access it by running time.sleep(2).
In your specific example, you used:
import time as zeit
you'll have to run:
zeit.sleep(2)
Alternatively, you can import sleep directly from time, by running:
from time import sleep
sleep(2)
Good luck!
You can read more about the time module here: https://docs.python.org/2/library/time.html
You can learn more about imports here: https://docs.python.org/3/reference/import.html
and I highly recommend learning about namespace in python, here: https://bytebaker.com/2008/07/30/python-namespaces/
I agree with #Wombatz on his solution, but I do not have enough reputation to comment on his question
One use case that I have found for importing a module within a class is when I want to initialize a class from a config file.
Say my config file is
config.py
__all__ = ['logfile', ... ]
logfile = 'myevent.log'
...
And in my main module
do_something.py
class event():
from config import *
def __init__(self):
try : self.logfile
except NameError: self.logfile = './generic_event.log'
Now the advantage of this scheme is that we do not need to import logfile in the global namespace if it is not needed
Whereas, importing at the beginning of do_something.py, I will have to use globals inside the class, which is a little ugly in my opinion.
It's probably a bit late, but I agree with idea of not polluting the module-level namespace (of course this can probably be remedied with a better design of a module, plus 'explicit is better than implicit' anyways).
Here is what I would do. The basic idea is this: import is an implicit assignment in which an entire module object gets assigned to a single name. Thus:
class Test:
import time as zeit
self.zeit = zeit # This line binds the module object to an attribute of an instance created from the class
def timer(self, count):
for i in range(count):
print("test "+str(i))
self.zeit.sleep(2) # This necessitates the `zeit` attribute within the instance created from the class
def doit(self):
self.timer(5)
import importlib
class importery():
def __init__(self, y,z):
self.my_name = y
self.pathy = z
self.spec = importlib.util.spec_from_file_location(self.my_name, self.pathy)
x = importlib.util.module_from_spec(self.spec)
self.spec.loader.exec_module(x)
print(dir(x))
root = x.Tk()
root.mainloop()
pathy = r'C:\Users\mine\Desktop\python310\Lib\tkinter\__init__.py'
importery('tk', pathy)
There is a 'time and a place' to do this type of black magic, thankfully very rare times and places. Of the few I've found I've normally been able to use subprocess to get some other flavor of python to do my dirty work, but that is not always an option.
Now, I have 'used' this in blender when I've needed to have conflicting versions of a module loaded at the same time. This is not a good way to do things and really should be a last resort.
If you are a blender user and you happen to decide to commit this sin, I suggest doing so in a clean version of blender, install a like version of python next to it to use that to do your pip installs with, and please make sure you have added your config folder to your blender folder, else this black magic may come back to bite you in the arse later.
Related
I am learning Python and am still a beginner, although I have been studying it for about a year now. I am trying to write a module of functions which is called within a main module. Each of the functions in the called module needs the math module to run. I am wondering if there is a way to do this without importing the math module inside the called module. Here is what I have:
main.py:
from math import *
import module1
def wow():
print pi
wow()
module1.cool()
module1.py:
def cool():
print pi
When running main.py I get:
3.14159265359
Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.cool()
File "Z:\Python\module1.py", line 3, in cool
print pi
NameError: global name 'pi' is not defined
What I'm having a hard time understanding is why I get a name error when running main.py. I know that the variable pi becomes global to the main module upon import because wow can access it. I also know that cool becomes global to the main module upon import because I can print module1.cool and get <function cool at 0x02B11AF0>. So since cool is inside the global namespace of the main module, shouldn't the program first look inside the function cool for the variable pi, and then when it doesn't find it there, look inside main module for the variable pi and find it there?
The only way to get around this that I know of is to import the math module inside module1.py. I don't like the idea of that, though because it makes things more complicated and I am a fan of nice, simple code. I feel like I am close to grasping namespaces, but need help on this one. Thanks.
As the traceback shows, the problem isn't in main.py, but in module1.py:
Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.cool()
File "Z:\Python\module1.py", line 3, in cool
print pi
NameError: global name 'pi' is not defined
In other words, in module1, there is no global name pi, because you haven't imported it there. When you do from math import * in main.py, that just imports everything from the math module's namespace into the main module's namespace, not into every module's namespace.
I think the key thing you're missing here is that each module has its own "global" namespace. This can be a bit confusing at first, because in languages like C, there's a single global namespace shared by all extern variables and functions. But once you get past that assumption, the Python way makes perfect sense.
So, if you want to use pi from module1, you have to do the from math import * in module1.py. (Or you could find some other way to inject it—for example, module1.py could do from main import *, or main.py could do module1.pi = pi, etc. Or you could cram pi into the magic builtins/__builtin__ module, or use various other tricks. But the obvious solution is to do the import where you want it imported.)
As a side note, you usually don't want to do from foo import * anywhere except the interactive interpreter or, occasionally, the top-level script. There are exceptions (e.g., a few modules are explicitly designed to be used that way), but the rule of thumb is to either import foo or use a limited from foo import bar, baz.
"Explicit is better than implicit" is a design decision that was made by the creators of Python (launch python and run import this).
Therefore, when you run module1.cool(), Python will not look for the undefined pi in the main module.
You'll have to import the math module in explicitly whenever you want to use it - that's just how Python works.
Also, you should avoid from X import *-style imports, that's bad practice too. Here, you could do: from math import pi.
As others have said, there isn't actually a global pi in your module1. A good solution for you is this, which only imports pi once from math and explicitly ensures that the pi you're getting is the one from module1:
main.py:
import module1
def wow():
print module1.pi
wow()
module1.cool()
module1.py:
from math import pi
def cool():
print pi
The simple approach of exec (python 3) or execfile (python 2) as mentioned in the comments by #abarnert may be useful for some workflows. All that is needed is to replace the import line with:
exec( open("module1.py").read() ) # python 3
and then you can simply call the function with cool() rather than module1.cool(). Within cool(), the variable pi will behave like a global, as the OP had originally expected.
In a nutshell, this is simply hiding a function definition that would otherwise appear at the top of your main program and has both advantages and disadvantages. For large projects with multiple modules and imports, using exec (instead of a proper namespaces) is probably a mistake as you don't generally want to keep too many things within a single global namespace.
But for simple cases (like using Python as a shell script) exec gives you a simple and concise way to hide shared functions while letting them share the global namespace. Just note that in this case you might want to give extra thought to how you name your functions (e.g. use v1_cool and v2_cool to keep track of different versions since you can't do v1.cool and v2.cool).
One less obvious disadvantage of using exec here is that errors in the executed code may not display the line number of the error although you can work around this: how to get the line number of an error from exec or execfile in Python
Inside the module you could simply define from math import pi, which would only import pi from math but not the entire math module.
I create the following package in eclipse via PyDev:
class Repository(object):
'''
classdocs
'''
def __init__(self):
'''
Constructor
'''
print("salaam")
class Materials(Repository):
'''
'''
def __init__(self):
'''
constructor
'''
My main file is:
if __name__ == '__main__':
pass
import repository;
x = Repository();
When i run my application, i get the following error:
x = Repository();
NameError: name 'Repository' is not defined
Of course, i got a warning on importing my module.
I know my import and relation of my main file and my package or eclipse configuration have problem.
first of all, when you import like this, you can only refer to your class as either repository.Repository or repository.repository.Repository, depending on the whether you import the module or the package.
second, what you import depends on where eclipse thinks you are. You can check that with
import os
print(os.pwd)
at the top of your main script.
third, if you want to import your package like this, you should put it in your search path. You can do that by placing it in site-packages, or for instance by adding
import sys
import os
sys.path.append(os.path.abspath(__file__))
at the top of your main script
additionally, you might want to avoid confusion by giving your module a different name than the package (or the other way round)
(and a little nitpick: __init__ is not the constructor, merely an initializing routine).
The import is wrong.
Instead of
import repository
you want to write for your case:
from repository.repository import Repository
As for PyDev giving the error, it's correct at this point and when you fix your code it should stop complaining.
Python won't allow me to import classes into eachother. I know there is no "package" solution in Python, so I'm not sure how this could be done. Take a look at my files' codes:
file Main.py:
from Tile import tile
tile.assign()
class main:
x = 0
#staticmethod
def assign():
tile.x = 20
file Tile.py:
from Main import main
main.assign()
class tile:
x = 0
#staticmethod
def assign():
main.x = 20
I get the error "class tile can not be imported".
If file A imports file B, and file B imports file A, they would keep importing each other indefinitely until the program crashed.
You need to rethink your logic in a way that doesn't require this circular dependency. For example, a 3rd file could import both files and perform both assignments.
You have your import backwards and the from needs to have the name of the module
from Tile import tile
Python begins executing Main.py. It sees the import, and so goes to execute Tile.py. Note that it has not yet executed the class statement!
So Python begins executing Tile.py. It sees an import from Main, and it already has that module in memory, so it doesn't re-execute the code in Main.py (even worse things would go wrong if it did). It tries to pull out a variable main from the Main module, but the class statement binding main hasn't executed yet (we're still in the process of executing the import statement, advice that line). So you get the error about there not being a clsss main in module Main (or Tile, if you started from there).
You could avoid that by importing the modules rather than importing classes out of the modules, and using qualified names, but then you'd fall one line down when Main.main doesn't work. Your code makes no sense I'm a dynamic language; you can't have both the definition of class main wait until after tile.assign has been called and the definition of class tile wait until after main.assign has been called.
If you really need this circular dependency (it's often, but not always a sign that something has gone wrong at the design stage), then you need to separate out "scaffolding" like defining classes and functions and variables from "execution", where you actually call classes and functions or use variables. Then your circular imports of the "scaffolding" will work even though none of the modules will be properly initialized while the importing is going on, and by the time you get to starting the "execution" everything will work.
I know that Python discourages any situation which can get you into a circular import. But I wanted understand the Python internals of why from-imports are seemingly arbitrarily less forgiving than normal imports in circular import situations.
For example, this code compiles:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
import commonutil
class Util:
# some code that uses commonutil.CommonUtil
pass
But this code does not:
# main.py
import CommonUtil
# commonutil.py
import util
class CommonUtil:
# some code that uses util.Util
pass
# util.py
from commonutil import CommonUtil
class Util:
# some code that uses CommonUtil
pass
Traceback (most recent call last):
File "main.py", line 1, in <module>
import CommonUtil
File "commonutil.py", line 1, in <module>
import util
File "util.py", line 1, in <module>
from commonutil import CommonUtil
ImportError: cannot import name CommonUtil
You don't hit compiler errors as long as you don't try to use the relevant classes before all the imports have completed. But when you try to do some aliasing, then it fails. Can someone explain what's going on internally in the Python that causes this error to rear its head only when from-import is used? And secondarily, is there any easy way around this? (Besides the obvious "pull shared code out to a third module," which I'll likely do anyways.)
Modules are executed from top to bottom. When an import is seen for the first time, execution of the current module is suspended so that the other module can be imported. When the other module attempts to import the first module, it gets a reference to the currently partially-executed module. Since code located after the import of the other module hasn't yet been executed, any names contained within it cannot yet exist.
main.py
import a
a.py
var1 = 'foo'
import b
var2 = 'bar'
b.py
import a
print a.var1 # works
print a.var2 # fails
The way around it is to not access the names in the imported module until its execution has completed.
see http://effbot.org/zone/import-confusion.htm#circular-imports for an explanation of what is happening.
I assume you launch the main.py file. Python will first try to load commonutil. It will create a module object, and start filling it with class and function and global variable when encountering their definition. The first statement is an import, so now python creates the util module and start filling it. The common module exist, but is empty. In the first version, you do not access any commonutil object at load time, so everything is fine. In the second one, you try to fetch a specific variable in commonutil which does not exist at this moment. If you had used something like f(commonutil.CommonUtil) in the first version, it would have also crashed.
Summary: when a certain python module is imported, I want to be able to intercept this action, and instead of loading the required class, I want to load another class of my choice.
Reason: I am working on some legacy code. I need to write some unit test code before I start some enhancement/refactoring. The code imports a certain module which will fail in a unit test setting, however. (Because of database server dependency)
Pseduo Code:
from LegacyDataLoader import load_me_data
...
def do_something():
data = load_me_data()
So, ideally, when python excutes the import line above in a unit test, an alternative class, says MockDataLoader, is loaded instead.
I am still using 2.4.3. I suppose there is an import hook I can manipulate
Edit
Thanks a lot for the answers so far. They are all very helpful.
One particular type of suggestion is about manipulation of PYTHONPATH. It does not work in my case. So I will elaborate my particular situation here.
The original codebase is organised in this way
./dir1/myapp/database/LegacyDataLoader.py
./dir1/myapp/database/Other.py
./dir1/myapp/database/__init__.py
./dir1/myapp/__init__.py
My goal is to enhance the Other class in the Other module. But since it is legacy code, I do not feel comfortable working on it without strapping a test suite around it first.
Now I introduce this unit test code
./unit_test/test.py
The content is simply:
from myapp.database.Other import Other
def test1():
o = Other()
o.do_something()
if __name__ == "__main__":
test1()
When the CI server runs the above test, the test fails. It is because class Other uses LegacyDataLoader, and LegacydataLoader cannot establish database connection to the db server from the CI box.
Now let's add a fake class as suggested:
./unit_test_fake/myapp/database/LegacyDataLoader.py
./unit_test_fake/myapp/database/__init__.py
./unit_test_fake/myapp/__init__.py
Modify the PYTHONPATH to
export PYTHONPATH=unit_test_fake:dir1:unit_test
Now the test fails for another reason
File "unit_test/test.py", line 1, in <module>
from myapp.database.Other import Other
ImportError: No module named Other
It has something to do with the way python resolves classes/attributes in a module
You can intercept import and from ... import statements by defining your own __import__ function and assigning it to __builtin__.__import__ (make sure to save the previous value, since your override will no doubt want to delegate to it; and you'll need to import __builtin__ to get the builtin-objects module).
For example (Py2.4 specific, since that's what you're asking about), save in aim.py the following:
import __builtin__
realimp = __builtin__.__import__
def my_import(name, globals={}, locals={}, fromlist=[]):
print 'importing', name, fromlist
return realimp(name, globals, locals, fromlist)
__builtin__.__import__ = my_import
from os import path
and now:
$ python2.4 aim.py
importing os ('path',)
So this lets you intercept any specific import request you want, and alter the imported module[s] as you wish before you return them -- see the specs here. This is the kind of "hook" you're looking for, right?
There are cleaner ways to do this, but I'll assume that you can't modify the file containing from LegacyDataLoader import load_me_data.
The simplest thing to do is probably to create a new directory called testing_shims, and create LegacyDataLoader.py file in it. In that file, define whatever fake load_me_data you like. When running the unit tests, put testing_shims into your PYTHONPATH environment variable as the first directory. Alternately, you can modify your test runner to insert testing_shims as the first value in sys.path.
This way, your file will be found when importing LegacyDataLoader, and your code will be loaded instead of the real code.
The import statement just grabs stuff from sys.modules if a matching name is found there, so the simplest thing is to make sure you insert your own module into sys.modules under the target name before anything else tries to import the real thing.
# in test code
import sys
import MockDataLoader
sys.modules['LegacyDataLoader'] = MockDataLoader
import module_under_test
There are a handful of variations on the theme, but that basic approach should work fine to do what you describe in the question. A slightly simpler approach would be this, using just a mock function to replace the one in question:
# in test code
import module_under_test
def mock_load_me_data():
# do mock stuff here
module_under_test.load_me_data = mock_load_me_data
That simply replaces the appropriate name right in the module itself, so when you invoke the code under test, presumably do_something() in your question, it calls your mock routine.
Well, if the import fails by raising an exception, you could put it in a try...except loop:
try:
from LegacyDataLoader import load_me_data
except: # put error that occurs here, so as not to mask actual problems
from MockDataLoader import load_me_data
Is that what you're looking for? If it fails, but doesn't raise an exception, you could have it run the unit test with a special command line tag, like --unittest, like this:
import sys
if "--unittest" in sys.argv:
from MockDataLoader import load_me_data
else:
from LegacyDataLoader import load_me_data