ホームページ > バックエンド開発 > Python チュートリアル > シングルトン パターンでのさまざまな言語でのさまざまな実装

シングルトン パターンでのさまざまな言語でのさまざまな実装

coldplay.xixi
リリース: 2020-10-14 17:37:20
転載
2301 人が閲覧しました

TodayPython ビデオ チュートリアル コラムでは、シングルトン モードでのさまざまな言語のさまざまな実装を紹介します。

シングルトン パターンでのさまざまな言語でのさまざまな実装

まえがき

少し前に、Python を使用してビジネスを実装する際に落とし穴を発見しました。 Python 素人が簡単に足を踏み入れる落とし穴;

おおよそのコードは次のとおりです:

class Mom(object):
    name = ''
    sons = []if __name__ == '__main__':
    m1 = Mom()
    m1.name = 'm1'
    m1.sons.append(['s1', 's2'])    print '{} sons={}'.format(m1.name, m1.sons)

    m2 = Mom()
    m2.name = 'm2'
    m2.sons.append(['s3', 's4'])    print '{} sons={}'.format(m2.name, m2.sons)复制代码
ログイン後にコピー

最初に Mom## のクラスを定義します#、文字列型の name とリスト型の sons 属性が含まれます。m1# を使用する場合、

は最初にこのクラスのインスタンスを作成します。 ## に進み、sons にリスト データを書き込みます。その後、インスタンス m2 を作成し、別のリスト データを sons に書き込みます。 あなたが

Javaer

Python をほとんど書かない場合、このコードを見たときに最初に思い浮かぶのは次のとおりです: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">m1 sons=[['s1', 's2']] m2 sons=[['s3', 's4']]复制代码</pre><div class="contentsignin">ログイン後にコピー</div></div>実際、最終出力は次のようになります。 結果は次のとおりです。

m1 sons=[['s1', 's2']]
m2 sons=[['s1', 's2'], ['s3', 's4']]复制代码
ログイン後にコピー

期待される値を達成したい場合は、わずかに変更する必要があります。

class Mom(object):
    name = ''

    def __init__(self):
        self.sons = []复制代码
ログイン後にコピー

の定義を変更するだけで済みます。

Python

関連の経験がなくても、これら 2 つのコードを比較すると、その理由を推測できるはずです。変数をインスタンス変数 (つまり、期待されるすべての出力) として使用するには、コンストラクターで変数を定義する必要があります。self 経由でアクセスします。

クラス内にのみ配置すると、Javastatic 静的変数と同様の効果が得られます。これらのデータはクラスによって共有されるため、この場合、

sons

Mom クラスで共有されるため、毎回蓄積されます。 Python シングルトンPython はクラス変数を介して同じクラス内で変数を共有する効果を実現できるため、シングルトン モードは実装できますか?

Python

metaclass 機能を使用して、クラスの作成を動的に制御できます。

class Singleton(type):
    _instances = {}    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)        return cls._instances[cls]复制代码
ログイン後にコピー
最初に

Singleton の基本クラスを作成し、それを実装する必要があるクラスで metaclass

class MySQLDriver:
    __metaclass__ = Singleton    def __init__(self):
        print 'MySQLDriver init.....'复制代码
ログイン後にコピー
として使用します。このようなシングルトン

SingletonMySQLDriver の作成を制御できます。実際、Singleton

__call__ はこのシングルトン プロセスの作成を簡単に理解できます。 : プライベート クラス属性 _instances (つまり、Java

map
    ) のディクショナリを定義します。これは Shared で実行できます。作成されるインスタンスの数に関係なく、クラス全体で。
  • カスタム クラスが __metaclass__ = Singleton を使用する場合、カスタム クラスの作成を制御できます。インスタンスが作成されている場合は、_instances## から直接作成します。オブジェクトを取得してそれを返します。それ以外の場合は、インスタンスを作成して _instances に書き戻します。これは、
  • Spring
  • コンテナーに似ています。
    if __name__ == '__main__':
        m1 = MySQLDriver()
        m2 = MySQLDriver()
        m3 = MySQLDriver()
        m4 = MySQLDriver()    print m1    print m2    print m3    print m4
    
    MySQLDriver init.....
    <__main__.MySQLDriver object at 0x10d848790>
    <__main__.MySQLDriver object at 0x10d848790>
    <__main__.MySQLDriver object at 0x10d848790>
    <__main__.MySQLDriver object at 0x10d848790>复制代码
    ログイン後にコピー
    最後に、実験結果から、シングルトンが正常に作成されたことがわかります。 Go Singletonチーム内の一部の企業が最近 go
  • を使い始めたので、
go

でシングルトンを実装する方法も知りたいです。例。

type MySQLDriver struct {
    username string}复制代码
ログイン後にコピー

このような単純な構造 (

Java

class として単純に理解できます) では、Python## に類似する方法はありません。 # および Java

はクラス共有変数を宣言することもできます;

go この言語には static の概念がありません。 しかし、同じ効果を達成するためにパッケージ内でグローバル変数を宣言することができます:

import "fmt"type MySQLDriver struct {
    username string}var mySQLDriver *MySQLDriverfunc GetDriver() *MySQLDriver {    if mySQLDriver == nil {
        mySQLDriver = &MySQLDriver{}
    }    return mySQLDriver
}复制代码
ログイン後にコピー
この方法で使用する場合:
func main() {
    driver := GetDriver()
    driver.username = "cj"
    fmt.Println(driver.username)

    driver2 := GetDriver()
    fmt.Println(driver2.username)

}复制代码
ログイン後にコピー
直接構築する必要はありませんMySQLDriver ですが、 GetDriver() 関数を通じて取得されます。

debug

を通じて、

driver

driver1# も確認できます。 ## は同じメモリアドレスを参照します。 この種の実装には問題はありません。賢い友人なら、Java のように、一度同時アクセスが行われれば、そうではないと考えるでしょう。単純。 。 go

では、複数の

groutinシングルトン パターンでのさまざまな言語でのさまざまな実装 が同時に

GetDriver()

にアクセスすると、複数の MySQLDriver 例が作成されます。

ここで述べられていることはそれほど単純ではありません。実際、これは Java に関連しています。go 言語は、簡単な api を提供します。重要なリソースへのアクセス。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">var lock sync.Mutexfunc GetDriver() *MySQLDriver {     lock.Lock()    defer lock.Unlock()    if mySQLDriver == nil {         fmt.Println(&quot;create instance......&quot;)         mySQLDriver = &amp;MySQLDriver{}     }    return mySQLDriver }func main() {    for i := 0; i &lt; 100; i++ {        go GetDriver()     }     time.Sleep(2000 * time.Millisecond) }复制代码</pre><div class="contentsignin">ログイン後にコピー</div></div>上記のコードを少し変更し、重要なリソースへのアクセスを単純に制御するための

lock.Lock()defer lock.Unlock()复制代码
ログイン後にコピー

コードを追加します。100 個のコルーチンの同時実行を有効にしても、mySQLDriver インスタンスは 1 回だけ初期化されます。 <ul><li>这里的 <code>defer 类似于 Java 中的 finally ,在方法调用前加上 go 关键字即可开启一个协程。

虽说能满足并发要求了,但其实这样的实现也不够优雅;仔细想想这里

mySQLDriver = &MySQLDriver{}复制代码
ログイン後にコピー

创建实例只会调用一次,但后续的每次调用都需要加锁从而带来了不必要的开销。

这样的场景每个语言都是相同的,拿 Java 来说是不是经常看到这样的单例实现:

public class Singleton {    private Singleton() {}   private volatile static Singleton instance = null;   public static Singleton getInstance() {        if (instance == null) {     
         synchronized (Singleton.class){           if (instance == null) {    
             instance = new Singleton();
               }
            }
         }        return instance;
    }
}复制代码
ログイン後にコピー

这是一个典型的双重检查的单例,这里做了两次检查便可以避免后续其他线程再次访问锁。

同样的对于 go 来说也类似:

func GetDriver() *MySQLDriver {    if mySQLDriver == nil {
        lock.Lock()        defer lock.Unlock()        if mySQLDriver == nil {
            fmt.Println("create instance......")
            mySQLDriver = &MySQLDriver{}
        }
    }    return mySQLDriver
}复制代码
ログイン後にコピー

Java 一样,在原有基础上额外做一次判断也能达到同样的效果。

但有没有觉得这样的代码非常繁琐,这一点 go 提供的 api 就非常省事了:

var once sync.Oncefunc GetDriver() *MySQLDriver {
    once.Do(func() {        if mySQLDriver == nil {
            fmt.Println("create instance......")
            mySQLDriver = &MySQLDriver{}
        }
    })    return mySQLDriver
}复制代码
ログイン後にコピー

本质上我们只需要不管在什么情况下  MySQLDriver 实例只初始化一次就能达到单例的目的,所以利用 once.Do() 就能让代码只执行一次。

シングルトン パターンでのさまざまな言語でのさまざまな実装

查看源码会发现 once.Do() 也是通过锁来实现,只是在加锁之前利用底层的原子操作做了一次校验,从而避免每次都要加锁,性能会更好。

总结

相信大家日常开发中很少会碰到需要自己实现一个单例;首先大部分情况下我们都不需要单例,即使是需要,框架通常也都有集成。

类似于 go 这样框架较少,需要我们自己实现时其实也不需要过多考虑并发的问题;摸摸自己肚子左上方的位置想想,自己写的这个对象真的同时有几百上千的并发来创建嘛?

不过通过这个对比会发现 go 的语法确实要比 Java 简洁太多,同时轻量级的协程以及简单易用的并发工具支持看起来都要比 Java 优雅许多;后续有机会再接着深入。

相关免费学习推荐:python视频教程

以上がシングルトン パターンでのさまざまな言語でのさまざまな実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート