剣指offer:面接問題2

10327 ワード

面接問題2:Singletonモードを実現
コンパイラ:python 3.5.2
プログラミング環境:pycharm 2018.1.2x64
方法一、_new__単一のモードを実現する方法
で_new__メソッド実装の一例パターン,例えば次のMyClassクラスは,クラスの初期化に影響を及ぼすのだろうか.クラスのインスタンスメソッド、クラスメソッド、静的メソッドに影響しますか?以下に私のこれらの概念に対する理解を述べて、もし間違いがあれば、交流の指摘を歓迎して、ここで感謝します.
__new__()は、Python 2において、新式クラスにおいて新たに出現する方法である.7以前のバージョンでは、クラスを定義するときに表示される継承objectが使用されていました.
objectは_new__()メソッドは、静的メソッドアクセラレータが付加されていなくてもクラスの静的メソッドとして定義される.さらに少なくとも1つの位置パラメータclsを渡す必要があり、clsはインスタンス化が必要なクラスを表し、このパラメータはインスタンス化時にPython解釈器によって自動的に提供される.
__new__メソッドが受け入れるパラメータと_init__同じですが_init__クラスインスタンスが作成された後に呼び出され、_new__()メソッドは、クラスが自己インスタンス化の準備をしているときに呼び出されます.new__メソッドは、このクラスインスタンスを作成する方法です.
まずobjectクラスの対__を見てみましょうnew__()メソッドの定義:
class object:

  @staticmethod # known case of __new__

  def __new__(cls, *more): # known special case of object.__new__

    """ T.__new__(S, ...) -> a new object with type S, a subtype of T """

    pass

objectは_new__()メソッドは静的メソッドとして定義されます.次のクラスのペア_new__()メソッドの実装:
class juli(object):
    
    def __init__(self):
        print("__init__() called...")
    def __new__(cls, *args, **kwargs):
        print('__new__()')
        return object.__new__(cls, *args,**kwargs)  ##       


if __name__=='__main__':

    a=juli()

#output:
#__new__()
#__init__() called...

インスタンス化されたオブジェクトが見つかったときに、__を呼び出します.init__()初期化前に__を呼び出しましたnew__()メソッド
__new__()は、インスタンス化されたインスタンスを返す戻り値が必要です.return親_new__()出てきた例は、objectの__を直接new__()のインスタンスが返されます.
__init__()パラメータselfがあります.このselfパラメータは__です.new__()返されるインスタンス、_init__()は_にありますnew__()の基礎の上でいくつかのその他の初期化の動作を完成することができて、_init__()値を返す必要はありません.
若_new__()現在のクラスclsのインスタンスが正しく返されていません.init__()は呼び出されません.親のインスタンスでもできません.
クラスをメーカーにたとえることができます.new__()方法は前期の原材料購入コーナーです.init__()方法は原材料がある上で、加工して、商品の一環を初期化することです.まずコードを見てみましょう.
class doubleFloat(float):

    def __new__(cls, *args, **kwargs):

        return float.__new__(cls, *args,**kwargs)
    def __init__(self, *args):
        print('=======')


a = doubleFloat()
print(a)
b = doubleFloat(1.9)
print(b)

インスタンスを挙げて、Personクラスを定義し、オブジェクトをインスタンス化するときに初期化パラメータをチェックし、正当であればインスタンスを作成し、正当でなければインスタンスを作成しないなどの用途を説明します.
class Person(object):
    def __new__(cls, name, age):
        if 0 < age < 150:
            return object.__new__(cls)
            # return super(Person, cls).__new__(cls)
        else:
            return None

    def __init__(self, name, age):
        self.name = name
        self.age = age



    def __str__(self):
        return '{0}({1})'.format(self.__class__.__name__, self.__dict__)


print(Person('Tom', 10))
print(Person('Mike', 200))

#Person({'name': 'Tom', 'age': 10})
#None

上記の例を通して、Pythonにおいて_new__メソッドと_init__方法は似ていますが、両方が存在する場合は_new__先に実行します.      
次にまとめます_new__と_init__の異同点:
1、両機能は似ていますが、どちらも存在する場合は_new__先に実行する;   
2,__new__メソッドはインスタンス化されたオブジェクトを返さなければなりません.     
3,__init__メソッドには戻り値がありません.      
4,__new__パラメータcls,_がありますinit__パラメータselfが__であるnew__返されるインスタンスオブジェクト.
日常的にPythonコードを作成する過程で、特にPython初心者は、このようなエラーに遭遇することが多い.TypeError: object() takes no parameters
上記のエラーについては、このエラー情報が明確に指摘されていないため、どのコードが問題を除いているのか迷いやすい.では、この間違いはどのようにして発生したのでしょうか.
pythonでは、メソッドはプロパティです.つまり、メソッドを呼び出すと、pythonはメソッド名に対応するプロパティが必要です.たとえば、次のようにします.
o.m()

pythonは、オブジェクトoでm属性を検索します.オブジェクトoにm属性がある場合(オブジェクトoにm属性があるかどうかを判断するにはhasattr関数で呼び出すことができます.)
しかし、pythonの方法はobjectではなくclassに定義されています.すなわち,mがoの方法であれば,その属性であるはずがない.通常、pythonはオブジェクトのプロパティを検索し、ない場合はクラスのプロパティを検索し、プロパティが存在する場合は呼び出すことができます.(ここではクラスとオブジェクトの2つの概念に混同される可能性がありますが、正確にはクラスはclassで、オブジェクトは実例で、具体的には文章の不器用な法学Pythonを見ることができます)
pythonでは、ほとんどのクラスがobjectから継承されています.Python 3では、継承objectを指定していない場合、解釈器が自動的に追加されますが、Pythonが指定していない場合はold-style classです.皆さんは普段クラスを書くときは、objectを継承することをお勧めします.これはコード互換性番号で、1つは優雅です.
このエラーは、オブジェクトインスタンスタイムズを作成したエラーです.たとえば、次のようにします.
class Foo(object):
    pass

もし私がそうなら:
f = Foo()

何の問題もありませんが、もし私がそうすれば:
f = Foo(10)

そして私は上の間違いを手に入れます.これはいったいなぜですか.
これは、Pythonがオブジェクトを作成するときに、2つのフェーズに分けられるためです.最初のフェーズでは、オブジェクトが呼び出し__を介しています.new__メソッドが作成されました.このメソッドの詳細はほとんど気にしません.new__メソッドは、オブジェクトインスタンスをすぐに返すわけではありません.new__メソッドの後に、__が呼び出されます.init__メソッドを使用して、オブジェクトに新しいプロパティを追加します.上のオブジェクトoに対して呼び出されるのは
o.__init__()

Pythonはまずoの__を探しますinit__メソッドが見つかりませんでした.親を検索します.init__メソッドでは、親が上のFooであると仮定し、_init__メソッドはまだ存在しないので、最後にobjectの__が見つかります.init__で行ないます.objectの_init__存在し、メソッドであり、その後、このメソッドを呼び出し、対応するパラメータを入力しますが、object._init__メソッドにはパラメータがなく、次のエラーが得られます.
TypeError: object() takes no parameters

プロセス全体で最も迷ったのは、Pythonがこのように間違っていないことです.
“object.__init__()” takes no parameters

そこで私たちはこの問題のためにどこにあるか法定ではありません.
まとめて、pythonのクラスを実装するときに、最後に__と書きます.init__方法は、このような迷いの誤りを避けることができる.
次に、単一のモードについて説明します.単一のモードは、クラスが1つのインスタンスしかないことを保証し、このインスタンスが独自に作成され、システムで使用されるのはすべてこのインスタンスです.単例モードは設計モードの一種であり、設計モードと単例モードのより具体的な内容については、関連する本を見ることができる.
リロードにより_new__実装例
class SingleTon(object):
    _instance = {}

    def __new__(cls, *args, **kwargs):
        if cls not in cls._instance:
            cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs)
        print(cls._instance)
        return cls._instance[cls]


class MyClass(SingleTon):
    class_val = 22

クラスの_new__()メソッドの使用は、初期化を行う前に、キャッシュにオブジェクトが存在するかどうかを確認し、存在する場合はキャッシュ格納オブジェクトを直接返し、存在しない場合は、次回の使用のためにオブジェクトをキャッシュに配置することです.
Pythonでは、_new__クラスのインスタンスを作成するために使用されます.init__このインスタンスを初期化するために使用されます.以上new__インスタンスを作成するには、対応するクラスのインスタンスを最後に返す必要があります.他のクラスのインスタンスを返すと、結果はどうなりますか.次のコードを参照してください.次のコードが実行されると、まずNoReturn_が印刷されます.new__そしてother instanceを印刷し、最後にtype(t)でtが見えるタイプは、もし_new__でこのクラスのインスタンスを返さないと呼び出せません_init__方法的.このクラスのインスタンスを返すには、以下のコードのreturn Other()をreturn super(NoReturn,cls)に変更する必要があります.new__(cls,*args,**kwargs)でいいです.
# output:NoReturn __new__
#        other instance
#        

class Other(object):
    val = 111

    def __init__(self):
        print('other instance')


class NoReturn(object):

    def __new__(cls, *args, **kwargs):
        print('NoReturn __new__')
        return Other()

    def __init__(self, a):
        print(a)
        print('NoReturn __init__')

t = NoReturn(66)
print(type(t))
#     
# output:NoReturn __new__
#         66
#         NoReturn __init__
#         

class Other(object):
    val = 111

    def __init__(self):
        print('other instance')


class NoReturn(object):

    def __new__(cls, *args, **kwargs):
        print('NoReturn __new__')
        return super(NoReturn, cls).__new__(cls)

    def __init__(self, a):
        print(a)
        print('NoReturn __init__')

t = NoReturn(66)
print(type(t))

#        

class SingleTon(object):
    _instance = {}

    def __new__(cls, *args, **kwargs):
        if cls not in cls._instance:
            cls._instance[cls] = cls._instance[cls] = super(SingleTon, cls).__new__(cls)
        print(cls._instance)
        return cls._instance[cls]

class MyClass(SingleTon):
    class_val = 22

    def __init__(self, val):
        self.val = val

    def obj_fun(self):
        print(self.val, 'obj_fun')

    @staticmethod
    def static_fun():
        print('staticmethod')

    @classmethod
    def class_fun(cls):
        print(cls.class_val, 'classmethod')


if __name__ == '__main__':
    a = MyClass(1)
    b = MyClass(2)
    print(a is b)   # True
    print(id(a), id(b))  # 4367665424 4367665424
    #     
    print(type(a))  # 
    print(type(b)) # 
    #     
    a.obj_fun()  # 2 obj_fun
    b.obj_fun()  # 2 obj_fun
    #    
    MyClass.class_fun()  # 22 classmethod
    a.class_fun()  # 22 classmethod
    b.class_fun()  # 22 classmethod
    #     
    MyClass.static_fun()  # staticmethod
    a.static_fun()  # staticmethod
    b.static_fun()  # staticmethod
    #    
    a.class_val = 33
    print(MyClass.class_val)  # 22
    print(a.class_val)  # 33
    print(b.class_val)  # 33
    #     
    print(b.val)  # 2
    print(a.val)  # 2

__new__シングル・モードの最終版を実現する方法
class SingleTon(object):
    _instance = {}

    def __new__(cls, *args, **kwargs):
        if cls not in cls._instance:
            cls._instance[cls] = super(SingleTon, cls).__new__(cls)
            # cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs)

        # print cls._instance
        return cls._instance[cls]



class MyClass(SingleTon):
    class_val = 22

    def __init__(self, val):
        self.val = val

    def obj_fun(self):
        print(self.val, 'obj_fun')


    @staticmethod
    def static_fun():
        print('staticmethod')

    @classmethod
    def class_fun(cls):
        print(cls.class_val, 'classmethod')


if __name__ == '__main__':
    a = MyClass(11)
    b = MyClass(22)
    print(a is b)   # True
    print(id(a), id(b)) # 4367665424 4367665424
    #     
    print(type(a)) # 
    print(type(b)) # 

最後に_new__メソッドが実装する単一のインスタンスモードは、インスタンスメソッド、クラスメソッド、静的メソッド、インスタンス変数、クラス変数に影響しますか?答えは対応する方法には影響しませんが、異なる変数でこのインスタンスを初期化し、後の変数でインスタンス変数とクラス変数を変更すると、前の変数も対応して変更されます.これもちょうど単一の例に合っています.いくつの変数がこのインスタンスを指しても、同じものを指しています.で_new__でインスタンスを生成した後、インスタンスオブジェクトを初期化するたびに同じインスタンスが生成されますが、このインスタンスでは関連するメソッド、変数、または通常のインスタンスと同じように使用されます.
方法二、装飾器を用いて単例モードを実現する
from functools import wraps


def single_ton(cls):
    _instance = {}

    @wraps(cls)
    def single(*args, **kwargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
        return _instance[cls]
    return single


@single_ton
class SingleTon(object):
    val = 123

    def __init__(self, a):
        self.a = a

if __name__ == '__main__':
    s = SingleTon(1)
    t = SingleTon(2)
    print(s is t)
    print(s.a, t.a)
    print(s.val, t.val)