How can I define static class members by using other static members?
for instance:
class Somefuncs:
#staticmethod
def foo():
print('foo was called')
functs_dict={'foo':Somefuncs.foo}
makes the interpreter raise the Exception: Unresolved reference 'Somefuncs'
even though I'm defining funct_dict within the class Somefuncs!
At this point the class is not really defined.
But you could just write:
class Somefuncs:
#staticmethod
def foo():
print('foo was called')
functs_dict={'foo': foo}
# Test it
Somefuncs.functs_dict["foo"]()
# Output: foo was called
It always refers to the current class
Related
If I have a staticmethod defined outside of a class, which I would like to inject into a class namespace using a custom type, then it appears that the class creation process strips away the staticmethod decorator. Is this expected behavior? Is there something larger I am not understanding about the class creation process and/or decorators?
from types import new_class
#staticmethod
def foo():
print("static method foo passed through __prepare__")
class FooMeta(type):
#classmethod
def __prepare__(mcs, *args, **kwargs):
return {"foo": foo}
Foo = new_class("Foo", bases=(), kwds={"metaclass": FooMeta}, exec_body=None)
class Foo2(metaclass=FooMeta):
...
class Foo3:
foo = staticmethod(foo)
print(foo)
print(Foo.foo)
print(Foo2.foo)
print(Foo3.foo)
The output
<staticmethod(<function foo at 0x7fee9995b400>)>
<function foo at 0x7fee9995b520>
<function foo at 0x7fee9995b520>
<staticmethod(<function foo at 0x7fee9995b520>)>
My class A has some methods that are, both, abstractmethods and staticmethods.
class A(ABC):
#staticmethod
#abstractmethod
def foo():
pass
#staticmethod
def bar():
A.foo()
Class B is a subclass of A and implements the abstractmethods.
class B(A):
#staticmethod
def foo():
print("B.foo")
When I call B.bar(), I'd expect to see "B.foo", however, when I debug the code, I see that A.foo() is called.
B.bar() # doesn't print anything
Can A.bar() call B.foo()'s implementation?
I am trying to create a wrapper class in Python with the following behaviour:
It should take as an argument an existing class from which it should inherit all methods and attributes
The wrapper class methods should be able to use Python super() to access methods of the superclass (the one passed as an argument)
Because of my second requirement I think the solution here will not suffice (and in any case I am having separate issues deepcopying some of the methods of the superclass' I am trying to inherit from).
I tried this but it's not correct...
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
class wrapper:
def __init__(self, super_class):
## Some inheritance thing here ##
# I initially tried this but no success...
super(super_class).__init__() # or similar?
def shout(self):
print('This is a wrapper')
super().shout()
And this is the behaviour I require...
my_wrapper = wrapper(A)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > I AM A
my_wrapper = wrapper(B)
my_wrapper.shout()
# Expected output:
# > This is a wrapper
# > My name is B!
Is inheritance the correct approach here, if so am I sniffing in the right direction? Any help is appreciated, thanks :)
Edit for context:
I intend to build multiple wrappers so that all of my ML models have the same API. Generally, models from the same package (sklearn for example) have the same API and should be able to be wrapped by the same wrapper. In doing this I wish to modify/add functionality to the existing methods in these models whilst keeping the same method name.
If wrapper has to be a class then a composition solution would fit much better here.
Keep in mind that I turned the shout methods to staticmethod because in your example you pass the class to wrapper.shout, not an instance.
class A:
#staticmethod
def shout():
print("I AM A!")
class B:
#staticmethod
def shout():
print("My name is B!")
class wrapper:
def __init__(self, super_class):
self._super_class = super_class
def __getattr__(self, item):
try:
return self.__dict__[item].__func__
except KeyError:
return self._super_class.__dict__[item].__func__
def a_wrapper_method(self):
print('a wrapper attribute can still be used')
my_wrapper = wrapper(A)
my_wrapper.shout()
my_wrapper = wrapper(B)
my_wrapper.shout()
my_wrapper.a_wrapper_method()
Outputs
This is a wrapper
I AM A!
This is a wrapper
My name is B!
a wrapper attribute can still be used
So I went for a function in the end. My final solution:
class A:
def shout(self):
print("I AM A!")
class B:
def shout(self):
print("My name is B!")
def wrap_letter_class(to_wrap):
global letterWrapper
class letterWrapper(to_wrap):
def __init__(self):
super().__init__()
def shout(self):
print('This is a wrapper')
super().shout()
def __getstate__(self):
# Add the wrapper to global scope before pickling
global letterWrapper
letterWrapper = self.__class__
return self.__dict__
return letterWrapper()
Which produces the desired behaviour...
In [2]: wrapped = wrap_letter_class(A)
In [3]: wrapped.shout()
This is a wrapper
I AM A!
In [4]: wrapped = wrap_letter_class(B)
In [5]: wrapped.shout()
This is a wrapper
My name is B!
Something not mentioned in my initial question was that I intended to pickle my custom class, this is not possible if the class is not defined in the global scope, hence the __getstate__ and global additions.
Thanks!
I need for an abstract class to be able to handle missing class method. I find out how to do this for instance method with __getattr__ but it's not working with class method. Is it even possible ?
I've got something like this :
class Container:
_definitions = {'class_a': 'example.class_a.ClassA',
'class_b': 'example.class_b.ClassB'}
#classmethod
def get(cls, def_id):
# import dynamicaly
parts = cls._definitions[def_id].split(".")
module_name = ".".join(parts[:-1])
class_name = parts[-1]
__import__(module_name)
return class_name
class AbstractClass:
#classmethod
def __getattr__(cls, name, *args):
def missing_method():
result = re.search("^(?P<class_id>[a-z0-9._-]*)$", name)
if result:
return Container.get(result.group('class_id'))
raise RuntimeError("class method '{}' missing from class".format(name))
return missing_method
class ClassA(AbstractClass):
#classmethod
def method(cls):
classb = cls.class_a()
classb.method()
class ClassB(AbstractClass):
#classmethod
def method(cls):
print('Hello world')
Each class in my Container must extend AbstractClass to be able to magically call any class in it, like I do un classA.method().
It's working if I do not use class method, but my purpose is that every class into my container can not be instantiate cause it will be useless for my needs. It's a kind of Singleton pattern.
Is it more understandable ?
The Production file (production_file.py) is:
class MyError(Exception):
pass
class MyClass:
def __init__(self):
self.value = None
def set_value(self, value):
self.value = value
def foo(self):
raise RuntimeError("error!")
class Caller:
def bar(self, smth):
obj = MyClass()
obj.set_value(smth)
try:
obj.foo()
except MyError:
pass
obj.set_value("str2")
obj.foo()
Test file (test.py):
import unittest
from unittest.mock import patch
from unittest.mock import call
from production_file import MyClass, Caller
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
my_class_mock_obj = MyClassMock.return_value
my_class_mock_obj.foo.side_effect = [MyError("msg"), "text"]
caller = Caller()
caller.bar("str1")
calls = [call("str1"), call("str2")]
my_class_mock_obj.set_value.assert_has_calls(calls)
if __name__ == '__main__':
unittest.main()
This above works. But if I move the production classes (MyError, MyClass, Caller) into the test file, and update patch to:
with patch('test.MyClass', autospec=MyClass) as MyClassMock:
then the instance method "foo" is no longer mocked.
Does anybody have any idea why that is?
I have also experienced a similar problem with some more complex code, where the production code is in my_package/src/production_file.py while the test is in my_package/tests/test_file.py. Python yields no error for the path, the path is correct, but still the mock doesn't work.
If you are running test.py as __main__ then it is not test.MyClass it would be __main__.MyClass, or in both cases __name__+".MyClass".
I was able to determine that the class used and the class patched were different by adding a print statement:
class Caller:
def bar(self, smth):
print(MyClass) #lets see what we are actually making an instance of...
obj = MyClass()
...
When the patch is applied to the class that this is using you would see something like this:
<MagicMock name='MyClass' spec='MyClass' id='4387629656'>
But when the class in moved into test.py you will see something like:
<class '__main__.MyClass'>
Which indicates:
There was no patching applied to MyClass (at least the one that is used for the test.)
The name of the class that needs to be patched is __main__.MyClass
However It is quite likely that your "more... complicated situation" is not working because of a setup like this:
from production_file import MyClass
class MyError(Exception):
pass
class Caller:
def bar(self, smth):
print(MyClass)
obj = MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...
In this case production_file.MyClass is being patched and MyClass is being imported from production_file so the correct class is being patched but still the output is:
<class 'production_file.MyClass'>
This is because the Class was directly imported to the local namespace, so when the patch is applied to the production_file the local namespace is still unaffected, we can check that the patch was actually applied with:
...
def bar(self, smth):
print(MyClass)
from production_file import MyClass as pf_MyClass
print(pf_MyClass)
...
#output:
<class 'production_file.MyClass'>
<MagicMock name='MyClass' spec='MyClass' id='4387847136'>
If this is the case you just need to import the module, not the class directly. Then once the patch is applied you will be using it right from the file:
import production_file
...
class Caller:
def bar(self, smth):
print(production_file.MyClass)
obj = production_file.MyClass()
...
class MyTest(unittest.TestCase):
def test_caller(self):
with patch('production_file.MyClass', autospec=MyClass) as MyClassMock:
...