零基础学习python需注意:不同语言对单例模式的不同实现

【python爬虫实战】:不同验证码的自动识别 本文介绍了如何用 Python 爬虫识别各种类型的验证码,包括文字验证码、图像验证码和滑动验证码。虽然这些方法在一定程度上可以帮助我们突破验证码的屏障,但请注意,滥用这些技术可能会导致账户被封禁或其他法律风险。请在合法合规的前提下使用这些技术。 阅读详情

前言

前段时间在用 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 看到这样的代码首先想到的输出应该是:

m1 sons=[['s1', 's2']]
m2 sons=[['s3', 's4']]
复制代码

但其实最终的输出结果是:

m1 sons=[['s1', 's2']]
m2 sons=[['s1', 's2'], ['s3', 's4']]
复制代码

如果想要达到期望值需要稍微修改一下:

class Mom(object):
    name = ''

    def __init__(self):
        self.sons = []
复制代码

只需要修改类的定义就可以了,我相信即使没有 Python 相关经验对比这两个代码应该也能猜到原因:

在 Python 中如果需要将变量作为实例变量(也就是每个我们期望的输出)时,需要将变量定义到构造函数中,通过 self 访问。

如果只放在类中,和 Java 中的 static 静态变量效果类似;这些数据由类共享,也就能解释为什么会出现第一种情况,因为其中的 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.....'
复制代码

这样Singleton 就可以控制 MySQLDriver 这个类的创建了;其实在 Singleton 中的 __call__ 可以很容易理解这个单例创建的过程:

  • 定义一个私有的类属性 _instances 的字典(也就是 Java 中的 map)可以做到在整个类中共享,无论创建多少个实例。
  • 当我们自定义类使用了 __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 单例

由于最近团队中有部分业务开始在用 go ,所以也想看看在 go 中如何实现单例。

type MySQLDriver struct {
	username string
}
复制代码

在这样一个简单的结构体(可以简单理解为 Java 中的 class)中是没法类似于 Python 和 Java 一样可以声明类共享变量的;go 语言中不存在 static 的概念。

但我们可以在包中声明一个全局变量来达到同样的效果:

import "fmt"

type MySQLDriver struct {
	username string
}

var mySQLDriver *MySQLDriver

func 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 中,如果有多个 goroutine 同时访问GetDriver() ,那大概率会创建多个 MySQLDriver 实例。

这里说的没那么简单其实是相对于 Java 来说的,go 语言中提供了简单的 api 便可实现临界资源的访问。

var lock sync.Mutex

func GetDriver() *MySQLDriver {
	lock.Lock()
	defer lock.Unlock()
	if mySQLDriver == nil {
		fmt.Println("create instance......")
		mySQLDriver = &MySQLDriver{}
	}
	return mySQLDriver
}

func main() {
	for i := 0; i < 100; i++ {
		go GetDriver()
	}

	time.Sleep(2000 * time.Millisecond)
}
复制代码

稍加改造上文的代码,加入了

lock.Lock()
defer lock.Unlock()
复制代码

代码就能简单的控制临界资源的访问,即便我们开启了100个协程并发执行,mySQLDriver 实例也只会被初始化一次。

  • 这里的 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.Once

func 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 优雅许多;后续有机会再接着深入。

参考链接:

Creating a singleton in Python

How to implement Singleton Pattern in Go

最后多说一句,想学习Python可联系小编,这里有我自己整理的整套python学习资料和路线,想要这些资料的都可以进q裙930900780领取。

本文章素材来源于网络,如有侵权请联系删除。

《小甲鱼—零基础入门学习python》学习过程(一) 学习日期:2017/09/13第一课:愉快的开始知识点汇总 1. Python的应用范围:操作系统、3D动画、WEB、企业应用、云计算等。 2. Python是什么类型的语言?脚本语言,即电脑编程语言,比C、C++或java之类的系统编程语言简单容易。 3. 什么是脚本语言? 一个脚本可以使得本来要用键盘进行的相互式操作自动化。一个shell脚本主要由原本需要在命令行输入的命令组成,或在一 阅读详情

相关推荐

Python从入门到实战:零基础到项目开发的完整学习指南

本文系统介绍了Python编程的学习路径,从基础语法到实战应用。主要内容包括:1)环境搭建与基础语法(变量、数据类型、控制流);2)核心语法(函数、面向对象、异常处理);3)实战项目(数据分析可视化、Flask博客开发);4)开发工具(虚拟环境、Git);5)AI辅助学习技巧。文章通过代码示例、流程图和项目案例,帮助读者构建完整的Python知识体系,适合从零基础到进阶的学习者。学习建议遵循"理论-实践-反思"循环,逐步深入特定领域开发。

zzywxc787的博客 1151

python语言和c语言哪个简单,python和c语言哪个更难

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

yyyqqq_的博客 1148

学习Python语言好还是C语言好

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

这家伙很懒,什么都没有留下 1110

c语言和python哪个更简单,c语言与python语言谁好用

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

aq8821的博客 944

【现代信号处理】01 -概率论与随机过程回顾

概率论与随机过程回顾 文章目录概率论与随机过程回顾1. 统计模型的相关概念1.1 统计实验(不确定性)1.2 样本点1.3 样本空间Ω1.4 概率1.5.统计模型是先验的2. 概率和统计的差异2.1 联系2.2 差异2.3.1 概率和统计问题的三要素2.3.1.1 model-模型2.3.1.2 data-数据2.3.1.3 decision-决策2.3.2 关系图3. 样本空间的重要性3.1 贝朗特悖论3.1.1 解法一3.1.2 解法二3.1.3 解法三3.2 结论4.概率相关的概念4.1 随机变量4.

qq_41741344的博客 1406

机器学习kaggle案例:风控评分卡模型(Give_Me_Some_Credit)

kaggle链接:https://www.kaggle.com/c/GiveMeSomeCredit ipynb文件:https://github.com/824024445/KaggleCases/blob/master/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0kaggle%E6%A1%88%E4%BE%8B%EF%BC%9A%E9%A3%8E%E6%8E%A7%...

weixin_42662126的博客 1万+

python和c语言哪个好上手,python和c语言哪个容易

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

2301_81837718的博客 1009

sysrepo 开发者指南(一个基于libyang与支持rpc的数据库)

sysrepo 开发者指南(一个基于libyang与支持rpc的数据库) 日志记录 默认情况下,所有 Sysrepo 日志记录都处于关闭状态。如果启用,日志消息有 3 种输出。它们可以写入stderr、由 处理syslog或简单地传递给回调。 请务必注意,启用 Sysrepo 日志记录也会修改libyang日志记录设置。错误消息被视为标准 Sysrepo 错误,其他严重性较低的消息完全隐藏。要更改此行为,只需在启用 Sysrepo 日志记录后调整libyang日志记录设置。 此外,几乎每个 Sysrepo

weixin_43489394的博客 9231

python和c语言哪个好上手,python和c语言哪个更难

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

yuuuuuuuu77的博客 839

论文Agile Autonomy: Learning High-Speed Flight in the Wild开源代码运行记录

​ Agile Autonomy: Learning High-Speed Flight in the Wild Learning High-Speed Flight in the Wild 论文链接:https://www.science.org/doi/10.1126/scirobotics.abg5810 代码:https://github.com/uzh-rpg/agile_autonomy 国内复现:https://github.com/Howardcl/AgileAutonomyPX4

Hanamizukimr的博客 1491

Win11系统下Abaqus2022与VS2022+oneAPI2023环境配置全攻略(附VUMAT子程序避坑指南)

本文详细指导在Windows 11系统下,为Abaqus 2022配置Visual Studio 2022与Intel oneAPI 2023的Fortran编译环境,以支持VUMAT等用户子程序开发。内容涵盖软件安装顺序、环境变量关联、快捷方式修改及关键验证步骤,并提供了首个VUMAT子程序的实战编写与模型验证方法,帮助用户高效搭建稳定可靠的仿真开发平台。

pytorch8learner的博客 319

python和c语言哪个好上手,python和c语言哪个好学?

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

2301_81896102的博客 689

QGis二次开发 —— 1、win10/win11搭建Vs2017-QGis3.40.12环境(附Vs2017环境效果,附全部问题解决方法)(附:Qt助手加入QGis接口说明文档)

QGis二次开发 —— 1、Windows10搭建Vs2017-QGis环境(附Vs2017环境效果)(附:Qt助手加入QGisA接口说明文档)

1362

计算机二级python和c语言,二级c语言和二级python

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

weixin_61088415的博客 1077

Windows10环境下激活虚拟环境报错【source : 无法将“source”项识别为 cmdlet、函数、脚本文件或可运行程序的名称】

在使用Django创建Web程序前,需要创建一个虚拟的工作环境,在使用【source ll_env/bin/activate】命令激活虚拟环境时报错:source : 无法将“source”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。所在位置 行:1 字符: 2。

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 1万+

fatal: unable to access ‘https://github.com/Stability-AI/stablediffusion.git/‘解决方案

本文主要介绍在安装 stable-diffusion-webui 时出现的 fatal: unable to access 'https://github.com/Stability-AI/stablediffusion.git/': Failed to connect to github.com port 443 after 21071 ms: Couldn't connect to server 问题的解决方案,希望能对你有所帮助。

水滴的博客 5688

零基础学习Python之条件判断_我的学习Python记录6

条件判断是编程中用来根据某些条件的真假(True或False)来执行不同代码块的一种结构。简单来说,就是“如果某个条件满足,就执行某段代码;否则,执行另一段代码”。

🚀免费专栏:✅穿越之灵脉重构师:重启天元代码系列✅Python筑基系列✅创新语言开发系列✅代码故事茶馆✅AI筑梦工作站 633

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

python和c语言哪个好上手,c语言和python哪个吃香

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

chatgpt001的博客 442

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

python 条件语句知识讲解_零基础学习 Python 之条件语句

写在之前大家好,这是发布在我公众号「Python空间」的第 16 篇文章,欢迎关注,期待和你的交流。大家好,这里是零基础学习 Python 系列,在这里我将从最基本的Python 写起,然后再慢慢涉及到高阶以及具体应用方面。我是完全自学的 Python,所以很是明白自学对于一个人的考验,所以在这里我会尽我最大的努力,把 Python 尽可能简单的表述清楚,让更多想要学习 Python 的朋友能够入...

weixin_39819152的博客 171

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

计算机二级c语言与python,二级c语言和二级python

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

YYmmsss的博客 922

Python零基础400集教程:从环境搭建到项目实战的完整学习路径

Python作为一门简洁高效的编程语言,其核心价值在于强大的生态和广泛的应用场景。从基础语法到高级应用,Python的学习路径遵循从环境搭建、语法掌握到项目实践的渐进过程。理解编程环境配置、核心语法概念以及模块化开发原理,是构建自动化脚本、数据分析工具和Web应用的技术基础。在工程实践中,掌握如PyCharm、VSCode等开发工具和Git版本控制,能有效提升开发效率。对于初学者,一套系统全面的教程至关重要,它能帮助学习者跨越环境配置、语法理解等初始门槛,并通过实战项目巩固知识。本文聚焦于如何高效利用长教程

weixin_30410119的博客 583

Python零基础学习路径:从环境搭建到项目实战的完整指南

编程语言的核心在于将逻辑转化为可执行的指令,Python以其简洁的语法和强大的生态成为初学者的理想选择。理解编程原理需要从环境配置开始,这是代码运行的基础,涉及解释器、编辑器和包管理工具的协同工作。掌握基础语法和数据结构(如列表、字典)后,便能构建解决实际问题的程序,其技术价值在于提升自动化处理能力和逻辑思维。在数据分析、Web开发、自动化脚本等应用场景中,Python的第三方库(如requests、pandas)极大地扩展了其能力边界。本文聚焦于为学习者提供一条从零开始的清晰路径,涵盖环境搭建、核心语法、

weixin_34208283的博客 423

python和c语言哪个好上手,c语言和python哪个更简单

本文探讨了学习Python和C语言的难处以及它们各自的应用场景,并比较了哪种编程语言更难学习。通过分析可以发现,无论是学习Python还是C语言都需要花费一定的时间和精力才能掌握它们。因此,选择一门适合自己的编程语言是非常重要的。无论你选择了哪一种编程语言,只要坚持不懈地学习和实践,相信你一定能够成为一名优秀的程序员。

2401_84667974的博客 1055
上一篇: 零基础小白python入门必看之高阶函数(2)
下一篇: 零基础小白学习python爬虫:如何在scrapy请求异常之后再设置代理IP
python阿喵
博客等级 码龄6年 169粉丝 127原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值