Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python objects from existing objects using __new__

In learning about Python's data model, I am playing with creating objects from existing objects using the __new__ method. Here are some examples which create new objects of various types:

x = 2;     print type(x).__new__(x.__class__)
x = {};    print type(x).__new__(x.__class__)
x = [1,2]; print type(x).__new__(x.__class__)
x = 2.34;  print type(x).__new__(x.__class__)
x = '13';  print type(x).__new__(x.__class__)
x = 1.0j;  print type(x).__new__(x.__class__)
x = True;  print type(x).__new__(x.__class__)
x = (1,2); print type(x).__new__(x.__class__)

However, the following three experiments give me errors:

x = None;           print type(x).__new__(x.__class__)
x = lambda z: z**2; print type(x).__new__(x.__class__)
x = object;         print type(x).__new__(x.__class__)

The errors are (respectively):

TypeError: object.__new__(NoneType) is not safe, use NoneType.__new__()
TypeError: Required argument 'code' (pos 1) not found
TypeError: type() takes 1 or 3 arguments

Why don't these three examples work? (Note: for the lambda example it appears that I have to pass in a code fragment when I invoke the __new__ method but I don't know how to do that.) I am using Python 2.6.

Please note that I realize this is not necessarily the way you would want to create new objects in real code, but my purpose is not practical, rather, it is to understand how the low-level object methods work.

like image 682
rlandster Avatar asked Sep 30 '12 05:09

rlandster


People also ask

What does __ new __ do in Python?

The __new__() is a static method of the object class. When you create a new object by calling the class, Python calls the __new__() method to create the object first and then calls the __init__() method to initialize the object's attributes.

What is __ add __ in Python?

The __add__() method in Python specifies what happens when you call + on two objects. When you call obj1 + obj2, you are essentially calling obj1.

Is __ new __ constructor in Python?

TLDR; __init() is not the constructor, __new__() is. Generally, developers new to python are told that __init__() method is called the constructor.

What is __ Getitem __ in Python?

The method __getitem__(self, key) defines behavior for when an item is accessed, using the notation self[key] . This is also part of both the mutable and immutable container protocols. Example: # Code to demonstrate use. # of __getitem__() in python.


1 Answers

It's nothing too special, it's just that for some types there is a default "empty" object of that type, while for others there is not. Your working examples are basically equivalent to:

int()
dict()
list()
float()
str()
complex()
tuple()

. . . all of which work. Your last three examples are basically trying to create new instances of NoneType, function, and type.

  1. NoneType fails for a unique reason, because None is a singleton --- the NoneType type can only ever have one instance, namely the None object. (The specific error message you get is a little strange, but if you do types.NoneType() you'll get a more direct message saying "Can't create NoneType instances".)
  2. function fails because, as you saw, it requires an argument, and you don't provide one. What you'd need is a code object, which you could get from an existing function or from the compile function. (It also requires a globals argument, which can just be a dict.)
  3. type fails because you didn't give enough arguments. You can either do type(foo) to get the type of foo, or type(name, bases, dict) to create a new type (i.e., a class).

Notice incidentally that in your last example you are taking the type of object, which is itself a type. If you do x = object() instead (making x be an individual object rather than the object type) then it will work and create a "blank" object.

The thing to remember is that calling __new__ is not really that magical. It is just what happens when you directly try to instantiate the type by doing someType(). If that type requires arguments, calling __new__ will fail, just like any other function call will fail if you don't give it the right arguments, because type(x).__new__ is just a function like any other function. You can see this with a user-defined class:

>>> class Foo(object):
...     pass
>>> x = Foo();           print type(x).__new__(x.__class__)
<__main__.Foo object at 0x00EB0370>
>>> class Foo(object):
...     def __init__(self, someArg):
...         pass
>>> class Foo(object):
...     def __new__(cls, someArg):
...         return super(Foo, cls).__new__(cls)
>>> x = Foo(2);           print type(x).__new__(x.__class__)
Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    x = Foo(2);           print type(x).__new__(x.__class__)
TypeError: __new__() takes exactly 2 arguments (1 given)

It succeeded in the first case, because my class didn't require arguments; it failed in the second class, because the second class does require arguments.

The __new__ does not fail for any secret reason; it just fails because the type you are trying to instantiate requires arguments in order to construct an instance. (The None case is the only one that's different here; that one does fail for a special reason, because None is a special object in Python.)

like image 154
BrenBarn Avatar answered Oct 11 '22 18:10

BrenBarn