python中__new__方法详解及使用

__new__的作用

  • __new__方法的作用是,创建并返回一个实力对象,如果__new__只调用了一次,就会得到一个对象,继承自object的新式类才有new这一魔法方法
  • 注意事项
  1. __new__是在一个对象实例化的时候所调用的第一个方法
  2. __new__至少必须要有一个参数cls,代表要实例化的类,此参数在实例化时由python解释器自动提供,其他的参数时用来直接传递给__init__方法
  3. __new__决定是否要使用该__init__方法,因为__new__可调用其他类的构造方法或者直接返回别的实力对象来作为本类的实例,如果__new__没有返回实例对象,则__init__不会被调用
  4. 在__new__方法中,不能调用自己的__new__方法,即:return cls.__new__(cls),否则报错(Recursionerror:maximum recursion depth exceeded:超过最大递归深度)

实例

class Animal(object):
  
    def __init__(self):
        self.color = color
    #如果不重写,__new__默认结构如下
    def __new__(cls,*args,**kwargs):
        return super().__new__(cls,*args,**kwargs)
        #return object.__new__(cls,*args,**kwargs)
tigger = Animal() #实例化过程中会自动调用__new__

在新式类中__new__才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用框架内的构造方法__init__进行操作

我们可以将类比作制造商,__new__()方法就是前期的原材料购买环节,__init__()方法就是在有原材料的基础上,加工,初始化商品环节。

单例模式

是一种常用的软件设计模式,目的:确保某一个类只有一个实例存在

如果希望在某个系统中,某个类只能出现一个实例的时候,那么这个单例对象就能满足要求

class DatabaseClass(object):
    def __new__(cls, *args, **kwargs):
# cls._instance=cls.__new__(cls) 不能使用自身的new方法
#容易造成一个深度递归,应该调用父类的new方法
if not hasattr(cls,'_instance'): cls._instance=super().__new__(cls, *args, **kwargs) return cls._instance pass db1 = DatabaseClass() print(id(db1)) db2 = DatabaseClass() print(id(db2)) db3 = DatabaseClass() print(id(db3))

 

1、__new__方法默认返回实例对象供__init__方法、实例方法使用。

class Foo(object):
    '''黄哥python培训,黄哥所写'''
    price = 50

    def how_much_of_book(self, n):
        print(self)
        return self.price * n

foo = Foo()
print(foo.how_much_of_book(8))
print(dir(Foo))

分析上面的代码,这个类实例化过程,Foo类继承object类,继承了object的__new__方法。

当你没有重写这个方法(通俗来说,你没有在Foo类中没有定义__new__方法),Foo实例化是默认自动调用父类__new__方法,这个方法返回值为类的实例(self),提供这个函数how_much_of_book,默认的第一个参数self。

class Foo(object):
    price = 50

    def __new__(cls, *agrs, **kwds):
        inst = object.__new__(cls, *agrs, **kwds)
        print(inst)
        return inst


    def how_much_of_book(self, n):
        print(self)
        return self.price * n

foo = Foo()
print(foo.how_much_of_book(8))
# <__main__.Foo object at 0x1006f2750>
# <__main__.Foo object at 0x1006f2750>
# 400

请看上面代码,Foo类中重载了__new__方法,它的返回值为Foo类的实例对象。

2、__init__ 方法为初始化方法,为类的实例提供一些属性或完成一些动作。

class Foo(object):

    def __new__(cls, *agrs, **kwds):
        inst = object.__new__(cls, *agrs, **kwds)
        print(inst)
        return inst


    def __init__(self, price=50):
        self.price = price

    def how_much_of_book(self, n):
        print(self)
        return self.price * n

foo = Foo()
print(foo.how_much_of_book(8))

# <__main__.Foo object at 0x1006f2750>
# <__main__.Foo object at 0x1006f2750>
# 400

3、__new__ 方法创建实例对象供__init__ 方法使用,__init__方法定制实例对象。

__new__ 方法必须返回值,__init__方法不需要返回值。(如果返回非None值就报错)

 

4、一般用不上__new__方法,__new__方法可以用在下面二种情况。

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

继承不可变数据类型时需要用到__new__方法(like int, str, or tuple) 。

class Inch(float):
    "Convert from inch to meter"
    def __new__(cls, arg=0.0):
        return float.__new__(cls, arg*0.0254)

print(Inch(12))

用在元类,定制创建类对象

class MetaClass(type):

    def __new__(meta, name, bases, dct):
        print '-----------------------------------'
        print "Allocating memory for class", name
        print meta
        print bases
        print dct
        return super(MetaClass, meta).__new__(meta, name, bases, dct)

    def __init__(cls, name, bases, dct):
        print '-----------------------------------'
        print "Initializing class", name
        print cls
        print bases
        print dct
        super(MetaClass, cls).__init__(name, bases, dct)


class Myclass(object):
    __metaclass__ = MetaClass

    def foo(self, param):
        print param


p = Myclass()
p.foo("hello")

# -----------------------------------
# Allocating memory for class Myclass
# <class '__main__.MetaClass'>
# (<type 'object'>,)
# {'__module__': '__main__', 'foo': <function foo at 0x1007f6500>, '__metaclass__': <class '__main__.MetaClass'>}
# -----------------------------------
# Initializing class Myclass
# <class '__main__.Myclass'>
# (<type 'object'>,)
# {'__module__': '__main__', 'foo': <function foo at 0x1007f6500>, '__metaclass__': <class '__main__.MetaClass'>}
# hello

 一个比较实际的例子,是在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__函数的作用是创建一个实例。
例子:

class A(object):
    def __init__(self):
        pass
print type(A)

结果:
<type 'type'>

Python type与objectpython当中的type是所有内置对象或者类的基类型,object是所有类继承的基类,因此int、str、list、tuple等等这些内置的类,这些都是type类的实例对象。因为type也是类,因此type的基类也是object

posted @ 2021-06-10 15:04  fat_girl_spring  阅读(4522)  评论(0编辑  收藏  举报