__new__是Python面向对象语言中一个很少用的函数,更多使用的是__init__这个函数。例如:

class Book(object):
def __init__(self, title):
super(Book, self).__init__(self)
self.title = title # Define a book b = Book('The Django Book')
print b.title
 

上面算是OOP语言的入门代码了,粗略一看__init__和java中的构造函数一样,其实不然,实际上它根本不能算的上构造函数。__new__才是创建实例的方法。

根据官方文档:

  • __init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值。

  • __new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例,是个静态方法。

也就是,__new__在__init__之前被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。

class Book(object):
def __new__(cls, title):
print '__new__'
return super(Book, cls).__new__(cls) def __init__(self, title):
print '__init__'
super(Book, self).__init__(self)
self.title = title b = Book('The Django Book')
print b.title
 

上面执行的结果:

__new__
__init__
The Django Book
 

__new__的应用场景

官方文档指出__new__方法的两种用法。

允许继承不可变类型(str,int, tuple)

关于这种也有比较多的例子,网上搜到的例子基本上都属于理论性,实际中用法不太常见。

在MetaClass中使用

MetaClass算是Python的语法糖吧,简单来说通过它可以动态生成或更改class的定义。

一个比较实际的例子,是在Django admin 表单验证的时候如何访问当前请求request。StackFlow的链接如下:

http://stackoverflow.com/questions/1057252/how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clean-met/6062628#6062628

首先想到的是把request也传递过去,在clean方法就可以使用了。

class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(MyForm, self).__init__(*args, **kwargs) def clean(self):
#这里可以得到self.request的信息
pass
 

在平常的view用下面的代码调用:

f = MyForm(request.POST, request=request)
 

但是在定制ModelAdmin的时候却不行,因为admin只提供get_form这个方法,返回值是类对象,而不是实例对象

get_form(self, request, *args, **kwargs):
# 这行代码是错误的
# return MyForm(request=request)
return MyForm # OK
 

用__new__方法可以解决这个问题。

def get_form(self, request, *args, **kwargs):
class ModelFormMetaClass(MyForm):
def __new__(cls, *args, **kwargs):
kwargs['request'] = request
return MyForm(*args, **kwargs)
return ModelFormMetaClass
 

那么结果如何呢,add_view的调用代码如下:

def add_view(self, request, form_url='', extra_context=None)"
...
ModelForm = self.get_form(request)
if request.method == 'POST':
form = ModelForm(request.POST, request.FILES)
#可以获取request参数
# print form.request
if form.is_valid():
pass
else:
pass
else:
...(计算initial)
form = ModelForm(initial=initial)
 

分析:form = ModelFormMetaClass(request.POST, request.FILES),按照通常的理解右边应该返回的是ModelFormMetaClass的一个实例,由于重写了__new__函数,没有调用父类函数,而是直接返回了一个带有request参数的MyForm实例,然后调用__init__函数,因此最后ModelFormMetaClass()返回也是这个实例,而左边也需要的是MyForm的实例对象。因此__new__函数的作用是创建一个实例。

备注:MetaClass它会降低代码的可读性,也有替代方案,不建议项目中使用。有兴趣的话可以参考这里

_new__和__init__的区别的更多相关文章

  1. 【转】python类中super()和__init__()的区别

    [转]python类中super()和__init__()的区别 单继承时super()和__init__()实现的功能是类似的 class Base(object): def __init__(se ...

  2. Python 中的__new__和__init__的区别

    [同] 二者均是Python面向对象语言中的函数,__new__比较少用,__init__则用的比较多. [异] __new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是 ...

  3. python__new__与__init__的区别

    __new__ __init__区别 1 class A(object): 2 def __init__(self,*args, **kwargs): 3 print "init A&quo ...

  4. python中super().__init__和类名.__init__的区别

    super().__init__相对于类名.__init__,在单继承上用法基本无差 但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次 多继承时 ...

  5. python类中super()和__init__()的区别

    class Base(object):     def __init__(self): print 'Base create' class childB(Base): def __init__(sel ...

  6. __init__ __new__区别

    请运行代码: class A: def __init__(self): print "A.__init" def __new__(self): print "A.__ne ...

  7. __new__和__init__的区别

    __new__是一个静态方法,而__init__是一个实例方法. __new__方法会返回一个创建的实例,而__init__什么都不返回. 只有在__new__返回一个cls的实例时后面的__init ...

  8. __new__() 与__init__()的区别

    __new__作用于__init__之前.前者可以决定是否调用后者,或者说可以决定调用那个类的__init__方法. 首先要知道在面向对象编程中,实例化基本遵循创建实例对象,初始化实例对象,最后返回实 ...

  9. 元类编程-- __new__和__init__的区别

    class User: def __new__(cls, *args, **kwargs): print (" in new ") return super().__new__(c ...

随机推荐

  1. 学习HTML5 全局属性 accesskey-title

    属性classcontextmenu指定一个元素的上下文菜单.当用户右击该元素,出现上下文菜单dirdropzone指定是否将数据复制,移动,或链接,或删除idspellcheck检测元素是否拼写错误 ...

  2. 编程语言-Java-问题整理

    jar文件运行报错 -  Exception in thread "main" java.lang.UnsupportedClassVersionError 低版本运行高版本文件 ...

  3. sqlserver关于时间的一些语句

    /* 去掉时间的时分秒 */ CONVERT(CHAR(10),operate_time,120) /* 所有的天数增加一天 */ DATEADD(day,1,t.operate_time) /* 返 ...

  4. LeetCode #1021. Remove Outermost Parentheses 删除最外层的括号

    https://leetcode-cn.com/problems/remove-outermost-parentheses/ Java Solution class Solution { public ...

  5. Shell05--函数应用

    目录 Shell05---函数应用 1. 函数基本概述 2. 函数基本使用 3. 函数参数传递 4. 函数状态返回 5. 函数场景示例 Shell05---函数应用 1. 函数基本概述 01. 什么是 ...

  6. 条款7:为多态基类析构函数声明为virtual

    基类指针指向子类对象. 子类对象必须位于堆.因此为了避免泄漏内存资源,当指针不使用时,delete掉每一个对象非常重要.但是如果基类的析构函数不声明为virtual.那么指向子类对象的指针delete ...

  7. standard_key.kmp

    [KeyRemap]keyVersion=2B33554467=[eraseeof]S36=[bof]B33554466=[pagedn]S35=[eof]B33554465=[pageup]B10= ...

  8. 【LeetCode】并查集 union-find(共16题)

    链接:https://leetcode.com/tag/union-find/ [128]Longest Consecutive Sequence  (2018年11月22日,开始解决hard题) 给 ...

  9. Sass函数:值列表函数length

    length() 函数主要用来返回一个列表中有几个值,简单点说就是返回列表清单中有多少个值: >> length(10px) 1 >> length(10px 20px (bo ...

  10. java一个对象使用内存