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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM