Methods called within __new__ - python

I would like to create a class which returns an int when initiated, like so:
r = Foo(10)
print r # 1000
I know you can do this by overriding the __new__ method. However I need it to also execute other class functions within the __new__ method, how do I do this?
So far I have:
class Foo(object):
def __new__(cls, i):
cls.i = i
return cls.foo_fun()
def foo_fun(self):
return self.i * 100
print Foo(5)
and the error I get:
Traceback (most recent call last):
return cls.foo_fun()
TypeError: unbound method foo_fun() must be called with Foo instance as first argument (got nothing instead)

You don't have an instance in your __new__ factory method (which is static, really). You don't have a self to call things on. Use another static or class method:
class Foo(object):
def __new__(cls, i):
return cls.foo_fun(i)
#staticmethod
def foo_fun(i):
return i * 100
print Foo(5)
Setting cls.i is not thread-safe as that state is shared between all __new__ calls; you are much better off passing along the value as a parameter to another method.
However, you are abusing classes here; you never create an instance of this class, there is no way to use the class in isinstance() type checks, etc. Just use a factory function:
def foo(i):
return i * 100
If you really meant for this to be a subclass of int, you'll still need to create an actual instance of your class to return:
class Foo(int):
def __new__(cls, i):
i = int(i) # ensure you have an actual integer first
value = cls.foo_fun(i)
return super(Foo, cls).__new__(cls, value)
#staticmethod
def foo_fun(i):
return i * 100
The above inherits from int, handles the case where the argument is not an integer (like"42"`, a string convertible to an integer) and returns an instance of your class.
Demo:
>>> class Foo(int):
... def __new__(cls, i):
... i = int(i) # ensure you have an actual integer first
... value = cls.foo_fun(i)
... return super(Foo, cls).__new__(cls, value)
... #staticmethod
... def foo_fun(i):
... return i * 100
...
>>> f = Foo(42)
>>> f
4200
>>> isinstance(f, Foo)
True
>>> Foo("42") # non-integer input works too
4200

Related

Property decorator with optional argument

I want to create a class property with a decorator that accepts an optional argument. Normally I would write
def MyProperty(func, optional=None):
def getter():
"""magic goes here"""
return func() if not optional else optional(func())
return property(getter)
class MyClass(object):
#MyProperty
def myFunction(foo):
return foo
MyClass().myFunction(5.)
>>> 5.0
This is all fine, but when I now also pass a function along the decorator like this:
class MyClass(object):
#MyProperty(int)
def myFunction(foo):
return foo
and I now call
MyClass().myFunction(5)
>>> TypeError: 'property' object is not callable
while I expect to get int(5) as result.
When you write
#MyProperty(int)
def myFunction(foo)
...
what that means is that MyProperty(int) is called, and whatever that returns is then called with myFunction as an argument. So MyProperty should be a function that returns a function that accepts a function and returns a function.
So you could write your decorator something like this:
def MyProperty(optional=None):
def decorator(func):
def getter(*args, **kwargs):
"""unspecified magic goes here"""
return func(*args, **kwargs) if not optional else optional(func(*args, **kwargs))
return getter
return decorator
So MyProperty(int) returns a function (decorator), and decorator returns whatever you are decorating.
However, when you call it without an argument, you'd still need to call it #MyProperty() instead of #MyProperty, otherwise you miss a stage of unwrapping.
>>> class MyClass:
... #MyProperty()
... def f1(foo):
... return foo
... #MyProperty(int)
... def f2(foo):
... return foo
...
>>> MyClass.f1(1.5)
1.5
>>> MyClass.f2(1.5)
1
I'm not sure about your use of property. Both your functions in the example are just functions inside a class. They don't have a self argument or a cls argument, and you're calling them from the class itself, not from an instance. It's somewhat unclear what you were aiming for.
When I tried this in Python 2 I had to declare the functions as static methods for this to work.
>>> class MyClass(object):
... #staticmethod
... #MyProperty()
... def f1(foo):
... return foo
... #staticmethod
... #MyProperty(int)
... def f2(foo):
... return foo
...
>>> MyClass.f1(0.5)
0.5
>>> MyClass.f2(1.5)
1

Python how to create a class that wraps any value

Let's say I have an Entity class:
class Entity(dict):
pass
def save(self):
...
I can wrap a dict object with Entity(dict_obj)
But is it possible to create a class that can wrap any type of objects, eg. int, list etc.
PS I have come up the following work around, it doesn't work on the more complex objects, but seems to work with basic ones, completely unsure if there are any gotchas, might get penalised with efficiency by creating the class every time, please let me know:
class EntityMixin(object):
def save(self):
...
def get_entity(obj):
class Entity(obj.__class__, EntityMixin):
pass
return Entity(obj)
Usage:
>>> a = get_entity(1)
>>> a + 1
2
>>> b = get_entity('b')
>>> b.upper()
'B'
>>> c = get_entity([1,2])
>>> len(c)
2
>>> d = get_entity({'a':1})
>>> d['a']
1
>>> d = get_entity(map(lambda x : x, [1,2]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/jlin/projects/django-rest-framework-queryset/rest_framework_queryset/entity.py", line 11, in get_entity
return Entity(obj)
TypeError: map() must have at least two arguments.
Improve efficiency:
EntityClsCache = {}
class EntityMixin(object):
def save(self):
...
def _get_entity_cls(obj):
class Entity(obj.__class__, EntityMixin):
pass
return Entity
def get_entity(obj)
cls = None
try:
cls = EntityClsCache[obj.__class__]
except AttributeError:
cls = _get_entity_cls(obj)
EntityClsCache[obj.__class__] = cls
return cls(obj)
The solution you propose looks elegant, but it lacks caching, as in, you'll construct a unique class every time get_entity() is called, even if types are all the same.
Python has metaclasses, which act as class factories. Given that metaclass' methods override these of class, not the instance, we can implement class caching:
class EntityMixin(object):
pass
class CachingFactory(type):
__registry__ = {}
# Instead of declaring an inner class,
# we can also return type("Wrapper", (type_, EntityMixin), {}) right away,
# which, however, looks more obscure
def __makeclass(cls, type_):
class Wrapper(type_, EntityMixin):
pass
return Wrapper
# This is the simplest form of caching; for more realistic and less error-prone example,
# better use a more unique/complex key, for example, tuple of `value`'s ancestors --
# you can obtain them via type(value).__mro__
def __call__(cls, value):
t = type(value)
typename = t.__name__
if typename not in cls.__registry__:
cls.__registry__[typename] = cls.__makeclass(t)
return cls.__registry__[typename](value)
class Factory(object):
__metaclass__ = CachingFactory
This way, Factory(1) performs Factory.__call__(1), which is CachingFactory.__call__(1) (without metaclass, that'd be a constructor call instead, which would result in a class instance -- but we want to make a class first and only then instantiate it).
We can ensure that the objects created by Factory are the instances of the same class, which is crafted specifically for them at the first time:
>>> type(Factory(map(lambda x: x, [1, 2]))) is type(Factory([1]))
True
>>> type(Factory("a")) is type(Factory("abc"))
True

How to apply a special methods 'Mixin' to a typing.NamedTuple

I love the typing.NamedTuple in Python 3.6. But there's often the case where the namedtuple contains a non-hashable attribute and I want to use it as a dict key or set member. If it makes sense that a namedtuple class uses object identity (id() for __eq__ and __hash__) then adding those methods to the class works fine.
However, I now have this pattern in my code in several places and I want to get rid of the boilerplate __eq__ and __hash__ method definitions. I know namedtuple's are not regular classes and I haven't been able to figure out how to get this working.
Here's what I've tried:
from typing import NamedTuple
class ObjectIdentityMixin:
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class TestMixinFirst(ObjectIdentityMixin, NamedTuple):
a: int
print(TestMixinFirst(1) == TestMixinFirst(1)) # Prints True, so not using my __eq__
class TestMixinSecond(NamedTuple, ObjectIdentityMixin):
b: int
print(TestMixinSecond(2) == TestMixinSecond(2)) # Prints True as well
class ObjectIdentityNamedTuple(NamedTuple):
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class TestSuperclass(ObjectIdentityNamedTuple):
c: int
TestSuperclass(3)
"""
Traceback (most recent call last):
File "test.py", line 30, in <module>
TestSuperclass(3)
TypeError: __new__() takes 1 positional argument but 2 were given
"""
Is there a way I don't have to repeat these methods in each NamedTuple that I need 'object identity' in?
The magic source of NamedTuple class syntax is its metaclass NamedTupleMeta, behind the scene, NamedTupleMeta.__new__ creates a new class for you, instead of a typical one, but a class created by collections.namedtuple().
The problem is, when NamedTupleMeta creating new class object, it ignored bases classes, you could check the MRO of TestMixinFirst, there is no ObjectIdentityMixin:
>>> print(TestMixinFirst.mro())
[<class '__main__.TestMixinFirst'>, <class 'tuple'>, <class 'object'>]
you have to extend NamedTupleMeta to take care of base classes:
import typing
class NamedTupleMetaEx(typing.NamedTupleMeta):
def __new__(cls, typename, bases, ns):
cls_obj = super().__new__(cls, typename+'_nm_base', bases, ns)
bases = bases + (cls_obj,)
return type(typename, bases, {})
class TestMixin(ObjectIdentityMixin, metaclass=NamedTupleMetaEx):
a: int
b: int = 10
t1 = TestMixin(1, 2)
t2 = TestMixin(1, 2)
t3 = TestMixin(1)
assert hash(t1) != hash(t2)
assert not (t1 == t2)
assert t3.b == 10

How to inherit a python generator and overwrite __iter__

I want to call the mother class but I get this message :
Traceback (most recent call last):
File "***test.py", line 23, in <module>
for i in daughter:
File "***test.py", line 18, in __iter__
for i in super(Mother, self):
TypeError: 'super' object is not iterable
I think it's just about the syntax, I try to call super(Mother, self) without any method, just the object itself.
Here the code :
class Mother(object):
def __init__(self, upperBound):
self.upperBound = upperBound
def __iter__(self):
for i in range (self.upperBound):
yield i
class Daughter(Mother):
def __init__(self, multiplier, upperBound):
self.multiplier = multiplier
super(Daughter, self).__init__(upperBound)
def __iter__(self):
for i in super(Mother, self): # Here
yield i * self.multiplier
daughter = Daughter(2, 4)
for i in daughter:
print i
Here it's just an exemple, my purpose is to read a file and yield line by line. Then a subclass generator parse all lines (for exemple make a list from the line...).
The proxy object returned by super() is not iterable just because there is an __iter__ method in the MRO. You need to look up such methods explicitly, as only that'll kick of a search:
for i in super(Daughter, self).__iter__():
yield i * self.multiplier
Note that you need to use super() on the current class, not the parent.
super() can't directly support special methods because these are looked up directly on the type by Python, not the instance. See Special method lookup for new-style classes:
For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.
type(super(Daughter, self)) is the super type object itself, and it doesn't have any special methods.
Demo:
>>> class Mother(object):
... def __init__(self, upperBound):
... self.upperBound = upperBound
... def __iter__(self):
... for i in range (self.upperBound):
... yield i
...
>>> class Daughter(Mother):
... def __init__(self, multiplier, upperBound):
... self.multiplier = multiplier
... super(Daughter, self).__init__(upperBound)
... def __iter__(self):
... for i in super(Daughter, self).__iter__():
... yield i * self.multiplier
...
>>> daughter = Daughter(2, 4)
>>> for i in daughter:
... print i
...
0
2
4
6

What is the hidden argument being passed to my `MethodType`?

I recently came across this recipe for making a "weakmethod" and thought it was the bees' knees; but there seems to be a mystery argument being passed to the resulting MethodType function that i can't seem to find:
from weakref import proxy
from types import MethodType
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
return MethodType(super(Foo, self).__getattribute__(name), proxy(self), self.__class__)
else:
return super(Foo, self).__getattribute__(name)
class Bar(Foo):
def my_func(self, a, b):
print a, b
def foo_my_func(self, a, b):
print 'FF Victory Theme'
>>> bar = Bar()
>>> bar.my_func(1, 2)
1 2
>>> weakmethod = bar.foo_my_func
>>> weakmethod(2, 3) # Or `bar.foo_my_func(2, 3)`
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
weakmethod(2, 3)
TypeError: foo_my_func() takes exactly 3 arguments (4 given)
What is this 4th argument that's being passed?
You used super(Foo, self).__getattribute__(name) to access the foo_my_func method. This already returns a MethodType object. You then wrap this object again.
So your returned object passes in proxy(self) to the wrapped method, which passes in another self argument. You started with a, b, and end up with self, proxy(self), a, b.
The recipe you linked to uses a decorator instead; this decorator is executed at class definition time, and wraps the function object. It is itself a descriptor, so it handles all the wrapping directly.
You'll want to either unwrap the result of super(Foo, self).__getattribute__(name) or don't use __getattribute__ at all.
Unwrapping can be done with accessing the __func__ attribute on a method:
class Foo(object):
def __getattribute__(self, name):
attr = super(Foo, self).__getattribute__(name)
if name.startswith('foo_'):
return MethodType(attr.__func__, proxy(self), self.__class__)
return attr
Not using __getattribute__ is done by just accessing the __dict__ mapping on the class directly:
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
for cls in type(self).__mro__:
if name in cls.__dict__:
return MethodType(cls.__dict__[name], proxy(self), self.__class__)
return super(Foo, self).__getattribute__(name)
where type(self).__mro__ lets you iterate over the class and it's base classes in method resolution order to manually search for the method.

Categories

Resources