How to convert string to class constructor [duplicate] - python

I have a class
class Foo():
def some_method():
pass
And another class in the same module:
class Bar():
def some_other_method():
class_name = "Foo"
# Can I access the class Foo above using the string "Foo"?
I want to be able to access the Foo class using the string "Foo".
I can do this if I'm in another module by using:
from project import foo_module
foo_class = getattr(foo_module, "Foo")
Can I do the same sort of thing in the same module?
The guys in IRC suggested I use a mapping dict that maps string class names to the classes, but I don't want to do that if there's an easier way.

import sys
getattr(sys.modules[__name__], "Foo")
# or
globals()['Foo']

You can do it with help of the sys module:
import sys
def str2Class(str):
return getattr(sys.modules[__name__], str)

globals()[class_name]
Note that if this isn't strictly necessary, you may want to refactor your code to not use it.

Related

Is it possible to force some functions to use different class during testing?

I have class with few methods. Let's say A
file a.py
class A:
def foo():
...
def bar():
...
during normal runtime (not testing) I use it in different modules, like that.
file functions.py
from a import A
def sample_function():
a_instance = A()
result = a_instance.foo()
return result
But during tests I would like to replace it with different class, let's say MockA.
file mock_a.py
class MockA:
# same methods name, but with different implementation
def foo():
...
def bar():
...
Now I would like to test module with some functionality
tests
from functions import sample_function
def test_sample_function():
assert sample_function() == expected_output
The QUESTION is:
Can I somehow "globally" set alias A = MockA (or do this in other way), so that during tests sample_function use functionality from MockA?
sample_function uses whatever A is bound to in the global namespace of the module functions. You can rebind your own class to that name.
from functions import one_function
class MockA:
...
functions.A = MockA
def test_sample_function():
assert sample_function() == expected_output
This is exactly what unittest.mock.patch is for
How about you use:
tests
from unittest.mock import patch
from mock_a import MockA
from functions import sample_function
#patch('functions.A', new_callable=MockA)
def test_sample_function(mocked_A):
assert sample_function() == expected_output
Try changeing this:
from a import A
into this:
from mock_a import MockA as A
First correct me in comments if I am wrong, but is it a mistake that you wrote one_function in your last code snippet, where you should have written sample_function.
I think your concern is that you don't want to change the functions.py code by replacing a_instance = A() with a_instance = MockA() everywhere in the code. So just make mock_a.py with same class name class A: and same methods name, but different implementations(like you said). All you will have to change in your functions.py code is from mock_a import A instead all all instance of class A() to class MockA(). This way I think your tests should work perfectly.

Why can't I import function from a class in the same directory?

In my builder/graph_builder.py, I have a class
class GraphBuilder(object):
def __init__(self):
pass
#staticmethod
def parse(path):
...
return path
Then in the same directory, I have a linker.py, and I want to import the 'parse' function:
from builder.graph_builder.GraphBuilder import parse
I am in PyCharm and it prompts that 'from builder.graph_builder.' is visible, but after that, it can't reference GraphBuilder and parse function.
Why is that?
The syntax is from MODULE import NAME, therefore
from builder.graph_builder import GraphBuilder
will work, but
from builder.graph_builder.GraphBuilder import parse
will not - builder.graph_builder.GraphBuilder is not a module.
It's not possible to import a Method from a Class in python
You have to, first import the class and after call the method. It happen even if it is a static method.
As graph_builder is located at same directory, you could refer graph_builder directly.
Furthermore GraphBuilder is a class, so you could instantiate and store parse function into a variable
You could try like this:
from graph_builder import GraphBuilder
if __name__=='__main__':
path="C:\\Users\\"
parse=GraphBuilder().parse
test=parse(path)

Inheriting a class with the same name in Python

I am new in Python and I am trying to create two classes with the same name in two different source files. Let’s call them "Main.py" and "Extension.py". The class is "MyClass". MyClass in Extesntion.py is derived from MyClass in file Main.py. If it works then when I create an object myclass and I import Extension in my code, then I would have more functions in comparison with file Main.py.
File Main.py
class MyClass:
def __init__(self):
Initialize something
def foo1(self, a, b):
Do something
Then extension would be like this:
File Extensions.py
import Main
class MyClass(MyClass):
def __init__(self):
Initialize something
def foo2(self, a, b):
Do something
def foo3(self, a, b):
Do something
And then if I have code like this. I expect that I can't use foo2 and foo3.
import Main
myclass = MyClass()
myclass.foo1(a, b)
And finally if I have code like this. I expect that I use all the functions.
import Extension
myclass = MyClass()
myclass.foo1(a, b)
myclass.foo2(a, b)
myclass.foo3(a, b)
If you do
import main
you'll need to use main.MyClass to create an object from main.py.
Instead you can do
from main import MyClass
to have it available directly.
If you need two different classes with the same name, you can instead do
from main import MyClass as MainClass
and you'll have the class available under the name MainClass
Unless you do from Extension import *, you'll need to specify the module in order to access the class.
import Main
import Extension
foo = Main.MyClass()
bar = Extension.MyClass()
If you don't want to have to specify the module, then the only way to avoid a name collision is to import the class under a different name like so:
from Main import MyClass as ClassA
It's quite easy when you explicitly import the given name using the from {module} import {name} syntax.
File main.py
class MyClass:
def __init__(self):
pass
def foo1(self, a, b):
pass
File extensions.py
from main import MyClass
class MyClass(MyClass):
def __init__(self):
pass
def foo2(self, a, b):
pass
def foo3(self, a, b):
pass
File client_main.py
from main import MyClass
myinstance = MyClass()
myinstance.foo1(a, b)
File client_extensions.py
from extensions import MyClass
myinstance = MyClass()
myinstance.foo1(a, b)
myinstance.foo2(a, b)
myinstance.foo3(a, b)
Generally in this case, you would do an import as. This allows you to alias your import as a new name. So in the file where your second class is, import the first class as:
from main import MyClass as MainMyClass
Then when doing your inheritance, refer to MainMyClass:
class MyClass(MainMyClass):

Use only class name without namespace in isinstance

This works in a script to recognise if a is of class myproject.aa.RefClass
isinstance(a, myproject.aa.RefClass)
But how could I do it so I do not have to specify the full namespace ? I would like to be able to type:
isinstance(a, RefClass)
How is this done in Python ?
EDIT: let me give more details.
In module aa.referencedatatable.py:
class ReferenceDataTable(object):
def __init__(self, name):
self.name = name
def __call__(self, f):
self._myfn = f
return self
def referencedatatable_from_tag(tag):
import definitions
defn_lst = [definitions]
for defn in defn_lst:
referencedatatable_instance_lst = [getattr(defn, a) for a in dir(defn) if isinstance(getattr(defn, a), ReferenceDataTable)]
for referencedatatable_instance in referencedatatable_instance_lst
if referencedatatable_instance.name == tag
return referencedatatable_instance
raise("could not find")
def main()
referencedata_from_tag("Example")
In module aa.definitions.py:
from aa.referencedatatable import ReferenceDataTable
#ReferenceDataTable("Example")
def EXAMPLE():
raise NotImplementedError("not written")
For some reason calling the main from aa.referencedatatable.py will throw as it will not be able to recognise the instance of the class. But if I copy this main in another module it will work:
import aa.referencedatatable
a = aa.referencedatatable.referencedatatable_from_tag("Example")
print a
This second example works, for some reason calling this function inside the same module where the class is declared does not.
The 'namespace' is just a module object, and so is the class. You can always assign the class to a different name:
RefClass = myproject.aa.RefClass
or better yet, import it directly into your own namespace:
from myproject.aa import RefClass
Either way, now you have a global name RefClass that references the class object, so you can do:
isinstance(a, RefClass)

Correct use of static methods

I have following simplified class named Password.py in folder1:
import random
CHARS = "ABC"
class PasswordHelper(object):
#staticmethod
def generateChars(length):
return ''.join(random.choice(CHARS) for x in range(length))
Now I have another class TestClass.py in folder2:
sys.path.append('../folder1/')
import Password
class Tester:
def whatever(self):
print Password.generateChars(3)
def main():
x = Tester()
x.whatever()
# call main method
main()
When calling python TestClass.py I get the following error: AttributeError: 'module' object has no attribute 'generateChars'. Both folders are on the same level. Is there a problem with the way I import the class files or with the static method declaration itself?
Python is not Java.
Firstly, there is absolutely no point to either the Tester or the Password classes. If you're not storing state, then don't define a class. Make both whatever and generateChars into normal standalone functions.
However, assuming you're doing this just to learn about Python classes, you have not understood that a class does not equal a module in Python. Since you've imported the Password module, you still need to refer to the PasswordHelper class:
Password.PasswordHelper.generateChars(3)
Alternatively, you can import the PasswordHelper class:
from Password import PasswordHelper
...
PasswordHelper.generateChars(3)
Finally, please follow PEP8 for your module, class and function names.
You defined the function in a class, so you need to reference it with the classname too:
print Password.PasswordHelper.generateChars(3)
Alternatively, move the function out of the class definition, at which point you do not need to use #staticmethod at all:
import random
CHARS = "ABC"
def generateChars(length):
return ''.join(random.choice(CHARS) for x in range(length))
In Python, functions do not have to be part of a class definition.

Categories

Resources