My question might seem stupid. But, I wish to know if there is a way in Python to make a Class itself callable. No I'm not talking about making a Class Instance callable. I'm talking about something like this:
class Foo:
def __call__(value):
return do_something_with(value)
#note I didn't use self because I want the class itself to be callable, not an instance of it
a = Foo(some_random_value)
But this doesn't seem to work. So, is there any way to do it or is it impossible to do in Python?
As explained above, and for probably all use cases, this is a very bad idea, don't do it! (puppies will die!)
class Foo:
def __new__(self, value):
return Foo.__call__(value)
#classmethod
def __call__(cls, value):
return f'processed {value}'
print(Foo(42))
output:
processed 42
Related
If have a class like this one:
class ExA:
def __new__(cls, obj):
if type(obj) is dict:
return ExADict(obj)
if type(obj) is list
return ExAList(obj)
Assuming I cannot change this class.
I want to inherit from it.
class ExB(ExA):
def new_method(self):
...
I figure I need to write a __new__ method that would handle this but I can't figure it out. I think I don't fully understand how super() works.
class ExB(ExA):
def __new__(cls, arg):
o = super().__new__(cls, list(arg))
"o" is an ExAList. I'm close. I now need to return the equivalent of class ExB(ExAList). But I don't get how to do so. (I know I could just go with class ExB(ExAList) and don't bother with __new__ but at this point, I just want to know!! :)
Edit: How would you re write ExA to make this easier?
Here is the code I am using
import funcy
#funcy.memoize
class mystery(object):
def __init__(self, num):
self.num = num
feat = mystery(1)
with open('num.pickle', 'wb') as f:
pickle.dump(feat,f)
Which is giving me the following error:
PicklingError: Can't pickle <class '__main__.mystery'>: it's not the
same object as __main__.mystery
I am hoping to 1) understand why this is happening, and 2) find a solution that allows me to pickle the object (without removing the memoization). Ideally the solution would not change the call to pickle.
Running python 3.6 with funcy==1.10
The problem is that you've applied a decorator designed for functions to a class. The result is not a class, but a function that wraps up a call to the class. This causes a number of problems (e.g., as pointed out by Aran-Fey in the comments, you can't isinstance(feat, mystery), because mystery).
But the particular problem you care about is that you can't pickle instances of inaccessible classes.
In fact, that's basically what the error message is telling you:
PicklingError: Can't pickle <class '__main__.mystery'>: it's not the
same object as __main__.mystery
Your feat thinks its type is __main__.mystery, but that isn't a type at all, it's the function returned by the decorator that wraps that type.
The easy way to fix this would be to find a class decorator meant that does what you want. It might be called something like flyweight instead of memoize, but I'm sure plenty of examples exist.
But you can build a flyweight class by just memoizing the constructor, instead of memoizing the class:
class mystery:
#funcy.memoize
def __new__(cls, num):
return super().__new__(cls)
def __init__(self, num):
self.num = num
… although you probably want to move the initialization into the constructor in that case. Otherwise, calling mystery(1) and then mystery(1) will return the same object as before, but also reinitialize it with self.num = 1, which is at best wasteful, and at worst incorrect. So:
class mystery:
#funcy.memoize
def __new__(cls, num):
self = super().__new__(cls)
self.num = num
return self
And now:
>>> feat = mystery(1)
>>> feat
<__main__.mystery at 0x10eeb1278>
>>> mystery(2)
<__main__.mystery at 0x10eeb2c18>
>>> mystery(1)
<__main__.mystery at 0x10eeb1278>
And, because the type of feat is now a class that's accessible under the module-global name mystery, pickle will have no problem with it at all:
>>> pickle.dumps(feat)
b'\x80\x03c__main__\nmystery\nq\x00)\x81q\x01}q\x02X\x03\x00\x00\x00numq\x03K\x01sb.'
You do still want to think about how this class should play with pickling. In particular, do you want unpickling to go through the cache? By default, it doesn't:
>>> pickle.loads(pickle.dumps(feat)) is feat
False
What's happening is that it's using the default __reduce_ex__ for pickling, which defaults to doing the equivalent of (only slightly oversimplified):
result = object.__new__(__main__.mystery)
result.__dict__.update({'num': 1})
If you want it to go through the cache, the simplest solution is this:
class mystery:
#funcy.memoize
def __new__(cls, num):
self = super().__new__(cls)
self.num = num
return self
def __reduce__(self):
return (type(self), (self.num,))
If you plan to do this a lot, you might think of writing your own class decorator:
def memoclass(cls):
#funcy.memoize
def __new__(cls, *args, **kwargs):
return super(cls, cls).__new__(cls)
cls.__new__ = __new__
return cls
But this:
… is kind of ugly,
… only works with classes that don't need to pass constructor arguments to a base class,
… only works with classes that don't have an __init__ (or, at least, that have an idempotent and fast __init__ that's harmless to call repeatedly),
… doesn't provide an easy way to hook pickling, and
… doesn't document or test any of those restrictions.
So, I think you're better off being explicit and just memoizing the __new__ method, or writing (or finding) something a lot fancier that does the introspection needed to make memoizing a class this way fully general. (Or, alternatively, maybe write one that only works with some restricted set of classes—e.g., a #memodataclass that's just like #dataclass but with a memoized constructor would be a lot easier than a fully general #memoclass.)
Another approach is
class _mystery(object):
def __init__(self, num):
self.num = num
#funcy.memoize
def mystery(num):
return _mystery(num)
I want to build a class, that is handling and processing some data. So I want property to handle all the data processing silently when new data is passed And I want to overload init with classmethod, to have flexibility on parameters passed to instance creation. So I came up with the following solution :
class Cooper():
def __init__(self):
...create all 'private' attributes
#classmethod
def Dougie(cls,data,datatype):
inst = cls.__new__(cls)
setattr(inst,datatype,data)
return inst
#property
def datatype1(self):
return self._datatype1
#datatype1.setter
def datatype1(self,newdata):
self._datatype1,self._datatype2,... = updatedata1(newdata)
#property
def datatype2(self):
return self._datatype2
#datatype2.setter
def datatype2(self,newdata):
self._datatype1,self._datatype2,... = updatedata2(newdata)
... to be continued...
Is this a pythonic way ? Or shoud I really create a metaclass (I get a little fir afraid there) ? What are the caveats ? Is there a better way ?
I'm using Python 3.
I know about the #classmethod decorator. Also, I know that classmethods can be called from instances.
class HappyClass(object):
#classmethod
def say_hello():
print('hello')
HappyClass.say_hello() # hello
HappyClass().say_hello() # hello
However, I don't seem to be able to create class methods dynamically AND let them be called from instances. Let's say I want something like
class SadClass(object):
def __init__(self, *args, **kwargs):
# create a class method say_dynamic
SadClass.say_dynamic() # prints "dynamic!"
SadClass().say_dynamic() # prints "dynamic!"
I've played with cls.__dict__ (which produces exceptions), and with setattr(cls, 'say_dynamic', blahblah) (which only makes the thingie callable from the class and not the instance).
If you ask me why, I wanted to make a lazy class property. But it cannot be called from instances.
#classmethod
def search_url(cls):
if hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url
Maybe because the property hasn't been called from the class yet...
In summary, I want to add a lazy, class method that can be called from the instance... Can this be achieved in an elegant (nottoomanylines) way?
Any thoughts?
How I achieved it
Sorry, my examples were very bad ones :\
Anyway, in the end I did it like this...
#classmethod
def search_url(cls):
if not hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url
And the setattr does work, but I had made a mistake when testing it...
You can add a function to a class at any point, a practice known as monkey-patching:
class SadClass:
pass
#classmethod
def say_dynamic(cls):
print('hello')
SadClass.say_dynamic = say_dynamic
>>> SadClass.say_dynamic()
hello
>>> SadClass().say_dynamic()
hello
Note that you are using the classmethod decorator, but your function accepts no arguments, which indicates that it's designed to be a static method. Did you mean to use staticmethod instead?
If you want to create class methods, do not create them in the __init__ function as it is then recreated for each instance creation. However, following works:
class SadClass(object):
pass
def say_dynamic(cls):
print("dynamic")
SadClass.say_dynamic = classmethod(say_dynamic)
# or
setattr(SadClass, 'say_dynamic', classmethod(say_dynamic))
SadClass.say_dynamic() # prints "dynamic!"
SadClass().say_dynamic() # prints "dynamic!"
Of course, in the __init__ method the self argument is an instance, and not the class: to put the method in the class there, you can hack something like
class SadClass(object):
def __init__(self, *args, **kwargs):
#classmethod
def say_dynamic(cls):
print("dynamic!")
setattr(self.__class__, 'say_dynamic', say_dynamic)
But it will again reset the method for each instance creation, possibly needlessly. And notice that your code most probably fails because you are calling the SadClass.say_dynamic() before any instances are created, and thus before the class method is injected.
Also, notice that a classmethod gets the implicit class argument cls; if you do want your function to be called without any arguments, use the staticmethod decorator.
As a side note, you can just use an instance attribute to hold a function:
>>> class Test:
... pass
...
>>> t=Test()
>>> t.monkey_patch=lambda s: print(s)
>>> t.monkey_patch('Hello from the monkey patch')
Hello from the monkey patch
How I achieved it:
#classmethod
def search_url(cls):
if not hasattr(cls, '_search_url'):
setattr(cls, '_search_url', reverse('%s-search' % cls._meta.model_name))
return cls._search_url
I'd like to be able to do this:
class A(object):
#staticandinstancemethod
def B(self=None, x, y):
print self is None and "static" or "instance"
A.B(1,2)
A().B(1,2)
This seems like a problem that should have a simple solution, but I can't think of or find one.
It is possible, but please don't. I couldn't help but implement it though:
class staticandinstancemethod(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
def newfunc(*args, **kw):
return self.f(obj, *args, **kw)
return newfunc
...and its use:
>>> class A(object):
... #staticandinstancemethod
... def B(self, x, y):
... print self is None and "static" or "instance"
>>> A.B(1,2)
static
>>> A().B(1,2)
instance
Evil!
Since you'd like the static method case to be used to create a new class anyway, you'd best just make it a normal method and call it at the end of the __init__ method.
Or, if you don't want that, create a separate factory function outside the class that will instantiate a new, empty object, and call the desired method on it.
There probably are ways of making exactly what you are asking for, but they will wander through the inner mechanisms of Python, be confusing, incompatible across python 2.x and 3.x - and I can't see a real need for it.
From what you're saying, is this along the line of what you're looking for?
I'm not sure there is a way to do exactly what you're saying that is "built in"
class Foo(object):
def __init__(self, a=None, b=None):
self.a
self.b
def Foo(self):
if self.a is None and self.b is None:
form = CreationForm()
else:
form = EditingForm()
return form
The answer to your question is no, you can't do that.
What I would do, since Python also supports regular functions, is define a function outside that class, then call that function from a normal method. The caller can decide what which one is needed.