1

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__.

R.O.S.S
  • 605
  • 5
  • 18
  • Why do you want to create this dynamically, rather than simply subclassing `float`? – Daniel Roseman Jan 22 '16 at 10:24
  • @DanielRoseman I thought that when I'm calling `type(..., (float,), {})` I'm subclassing that type, no? I'm pretty new to that, sorry if it's obvious for you. – R.O.S.S Jan 22 '16 at 10:25
  • Yes but what's the point? Why not do the standard `class _float(float):...`, and define the `__len__` method normally on that class? – Daniel Roseman Jan 22 '16 at 10:26
  • @DanielRoseman I want to add the `__len__` attribute dynamically inside my module and create that module portable, so the user doesn't need to define his custom class (using statement). I also want the `__len__` to act exactly as I want, so I want it also redefined if user defined it itself. – R.O.S.S Jan 22 '16 at 10:29
  • 1
    @R.O.S.S Some example usage would be good. From the description I can't tell if you're trying to do something really cool, or something very silly and over the top... Can you add the full example of `_float` and how it's supposed to be used? (and show which bits you mean as "portable" / "custom class") – viraptor Jan 22 '16 at 10:35
  • @viraptor You can see `_float`'s declaration in the question, its use is that it takes a `str` and converts it into `float` (e.g. having all attributes of `float`) and on top it has a `__len__` (the number of digits in the number) I tried [this](http://stackoverflow.com/a/2982/4642081) but it didn't help. – R.O.S.S Jan 22 '16 at 10:41

3 Answers3

1

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'
Wombatz
  • 4,958
  • 1
  • 26
  • 35
1

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.

Matthew
  • 7,440
  • 1
  • 24
  • 49
0

Maybe this is enough:

class _float(float):
    def __len__(self):
        return len(str(self))

Doesn't use dynamic type.

viraptor
  • 33,322
  • 10
  • 107
  • 191