Reputation: 625
I'm trying to create object dynamically using type
. Specifically, I want to create a float which has a len
.
This is what I tried:
_float = type('_float', (float,), {})
Another file, where's the instance created using function (constructor) reference:
obj = x[1](x[0]) # `x` is a 2-length tuple: (value, function)
obj.old = x[0]
obj.__len__ = lambda: len(x[0]) # I'm not sure if this lambda should take argument 'self'
out.append(obj) # 'out' is a list of output
This code is in loop, and when it's done, I run following (for testing where's the bug):
print(out[0].old) # => 1, correct
print(out[0].__len__) # <function bla.bla.<lambda> at: 0xblabla>
print(out[0].__len__()) # 1, correct (number of digits)
print(len(out[0])) # TypeError: object of type '_float' has no len()
The basic idea is that the user imports my module, gives it a str
value and a function. My module's object then applies that function to the str
value and returns a resulting object. However, it must keep the original value or at least its __len__
.
Upvotes: 1
Views: 309
Reputation: 7590
This is one of the differences between python 3 and python 2. This would work in python 2 (using "old style" classes), but fails in python 3.
For example, in python 2 the following works:
class test: pass # "old style" class
a = test()
a.__len__ = lambda: 10
len(a) # 10
but this doesn't
class test(object): pass # inherit from object, "new style" class
a = test()
a.__len__ = lambda self: 10
len(a) # TypeError: object of type 'test' has no len()
and it doesn't work in python 3 as all classes are "new style".
Essentially, in new style classes anything that uses a built in overloaded method (methods with double underscores on both sides) will bypass the instance altogether, and go straight to the class.
As long as you define the method on the class, it works fine. For example (python 3)
class test: pass
test.__len__ = lambda self: 10
a = test()
len(a) # 10
In fact, you can even create the method after instance creation
class test: pass
a = test()
a.__class__.__len__ = lambda self: 10
len(a) # 10
A search for "python new style classes method lookup" should give you more information than you could ever want on this. Additionally, chapters 32 and 38 of Learning Python, 5th edition (Oreilly, 2013) discusses these differences in depth.
Upvotes: 1
Reputation: 34205
Maybe this is enough:
class _float(float):
def __len__(self):
return len(str(self))
Doesn't use dynamic type.
Upvotes: 0
Reputation: 5458
The reason why it does not work is explained here
TL;DR You are adding the __len__
method to the instance dict and not to the class itself.
Try this:
_float.__len__ = lambda self: YOUR_LEN
Then this works:
print(len(_float(1)))
But as the comments suggest subclassing float by defining a class the normal way is somewhat less hacky.
Edit:
If you want your __len__
method to return the length of the old
attribute you can do it like this:
_float.__len__ = lambda self: self.old
This might fail if len
is called on an object without an old
attribute
AttributeError: '_float' object has no attribute 'old'
Upvotes: 1