Error when pickling dynamically defined extension of an abstract base class - python

I would like to persist an object that is created from a dynamically defined class that extends an abstract base class. I have tried to achieve this by pickling the object. I am using Python 3.6.8
The error message suggests that pickle is trying to look for the class I have created within the abc module and is unable to find it there. I have tried to pickle by creating the class within the main module (by defining class_factory inside test.py) but I get the same error message.
objmodel.py
from abc import ABC, abstractmethod
class mtype(ABC):
def __init__(self):
super().__init__()
#abstractmethod
def set_someattrib(self, attrval):
pass
#classmethod
def set_attrib1(cls, aval):
cls.attrib = aval
utils.py
from objmodel import *
def class_factory(mname, aval):
c1 = type(mname, (mtype,), {"set_someattrib": set_attrib1})
c1.set_someattrib(aval)
return c1
test.py
from utils import class_factory
import pickle
c1 = class_factory('C', 1)
print(c1.attrib)
m = c1()
fh = open('objtest', 'wb')
pickle.dump(m, fh)
I get the following error:
_pickle.PicklingError: Can't pickle : attribute lookup C on abc failed

Related

Python dynamically import classes an call their functions

I have one Abstract Animal class.
from abc import ABC, abstractmethod
class Animal(ABC): # Inherit from ABC(Abstract base class)
#abstractmethod # Decorator to define an abstract method
def feed(self):
pass
And three classes which Implements it
from Animal import Animal
class Lion(Animal):
def feed(self):
print("Feeding a lion with raw meat!")
from Animal import Animal
class Panda(Animal):
def feed(self):
print("Feeding a panda with some tasty bamboo!")
from Animal import Animal
class Snake(Animal):
def feed(self):
print("Feeding a snake with mice!")
Now I just want to import all classes, which are in the project folder and call the feed function of all classes, when there is a feed function.
from glob import glob
import os
if __name__ == '__main__':
for file in glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), "*.py")):
name = os.path.splitext(os.path.basename(file))[0]
# add package prefix to name, if required
module = __import__(name)
try:
module.feed()
except Exception as e:
print(e)
My Problem is now, that I get the errors:
module 'Animal' has no attribute 'feed'
module 'Lion' has no attribute 'feed'
module 'main' has no attribute 'feed'
module 'Panda' has no attribute 'feed'
module 'Snake' has no attribute 'feed'
Can someone help me with this?
I take it your files are called Snake.py, Panda.py etc. If so then you are invoking feed() on the files not the classes. You need to get the modules (which you've done), then get the class and then call the method:
from glob import glob
import os
if __name__ == '__main__':
for file in glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), "*.py")):
name = os.path.splitext(os.path.basename(file))[0]
if name == "Animal" or name == "main": # avoid main.py and Animal.py
continue
# add package prefix to name, if required
module = __import__(name)
try:
# get the class
cls = getattr(module, name)
# instantiate the class and call the method
cls().feed()
except Exception as e:
print(e)
Ive also included a safety check to exclude main.py and Animal.py

Python import that works with directory and package

I have two classes (in the same directory), one derived from the other.
ClasssA.py contains the following code:
class ClassA():
def __init__(self):
pass
ClasssB.py contains the following code:
from ClassA import ClassA
class ClassB(ClassA):
def __init__(self):
ClassA.__init__(self)
This works fine when I run scripts from the Python console.
I want to build these into a package, but when I do and try to instantiate ClassB, I get an error:
No module named 'ClassA'
How can I write the import statement and/or __init__.py so the code works in both scenarios.
Thanks for any suggestions.
1) Create a module named classA containing two files init.py(Empty file) and temp.py
temp.py
class ClassA():
def __init__(self):
print('ClassA')
2) ClassB.py
from ClassA.temp import ClassA
class ClassB(ClassA):
def __init__(self):
super.__init__()
All this should in same folder
Use from filename import ClassA to indicate which file in the directory you are importing from.

Serialize subclass of abstract class in python 3.7.1

I have the following script:
from abc import ABC, abstractmethod
import dill
class A(ABC):
#abstractmethod
def test(self, a):
"""Abstract method"""
class B(A):
def test(self, a):
return a + 1
if __name__ == '__main__':
test_obj = B()
with open('test_save.pkl', 'wb') as f:
dill.dump(test_obj, f)
When I run this script in a python 3.6 environment, it runs successfully. When I run it in a python 3.7.1 environment, it errors with:
TypeError: can't pickle _abc_data objects
One work around I have found is to move the class definition of B into a separate file and import it - then saving works. HOWEVER, assuming I do NOT want to do that:
1) is there a way to serialize with dill a subclass of an abstractclass defined in the same file
2) what changed between python 3.6 and python 3.7 that caused this?

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)

Categories

Resources