Python学习6(单例模式模块包)

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习6(单例模式模块包)相关的知识,希望对你有一定的参考价值。

单例模式

举个常见的单例模式例子,我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。

这里用两个私有化的变量来控制单例的产生
在new方法中,用instance变量来控制只能产生一块内存地址
在init方法中,用first变量来控制这个内存地址的对象是否已经被赋值了,如果已经赋值了,那么再创建对象的时候就不能进行改变了
这样保证了只能产生一个对象,只能在这个对象基础上进行更改,而不能新建一个对象

如果去掉is_first变量,那么只有一个内存地址,但是新建对象可以对内存地址中的内容进行更改,即可以通过init方法更改属性;加上这个变量判断以后,就不能进行对象的创建了

# 实例化一个单例
class Singleton(object):
    # 私有化
    __instance = None
    __is_first = True

    # 正常情况下,会调用new方法创建实例,而如果想阻止这个操作,就需要在new方法里实现
    # 这里判断这个类的实例对象是否已经存在,如果不存在可以创建,如果存在了就返回这个实例对象
    def __new__(cls, age, name):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self, age, name):
        if self. __is_first: # 不会再创建第二个对象
	        self.age = age
	        self.name = name
	        Singleton. __is_first = False


a = Singleton(18, "张三")
b = Singleton(28, "张三")
print(a)  # <__main__.Singleton object at 0x000001D6AAF544E0>
print(b)  # <__main__.Singleton object at 0x000001D6AAF544E0>

print(a.age) # 18
print(b.age) # 18

a.age = 19
print(b.age)    # 19

模块

在Python中有一个概念叫做模块(module)。

说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块

比如我们经常使用工具 random,就是一个模块。使用 import random 导入工具之后,就可以使用 random 的函数。

好处:
1.提高代码的可复用,可维护性,一个模块编写完毕后,可以很方便的在其他项目中导入
2.解决了命名冲突问题,不同模块中相同命名不会冲突

导入模块

导入模块有四种方式

import 模块名

import 模块1,模块2,...  # 导入方式

模块名.函数名()  # 使用模块里的函数

注意:必须加上模块名调用
因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名

from 模块名 import 功能名

有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:

from 模块名 import 函数名1,函数名2....

不仅可以引入函数,还可以引入一些全局变量、类等

注意:通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。

from 模块名 import *

把一个模块的所有内容全都导入到当前的命名空间也是可行的,调用的时候也不需要加模块名字,只需使用如下声明:

from modname import *

注意:这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

import 模块名 as 别名 / from 模块名 import 功能名 as 别名

In [1]: import time as tt  # 导入模块时设置别名为 tt

In [2]: time.sleep(1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-07a34f5b1e42> in <module>()
----> 1 time.sleep(1)

NameError: name 'time' is not defined

In [3]: 

In [3]: tt.sleep(1)  # 使用别名才能调用方法

In [4]: 

In [4]: from time import sleep as sp  # 导入方法时设置别名

In [5]: sleep(1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-82e5c2913b44> in <module>()
----> 1 sleep(1)

NameError: name 'sleep' is not defined

In [6]: 

In [6]: sp(1)  # 使用别名才能调用方法

__name__

需要注意,在导入的时候会将模块的内容加载,所以如果模块中有测试代码的会,也会被执行
所以为了能达到测试的目的,又在其他项目中导入时不会执行测试代码,需要如下操作:
在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息,例如:

test1.py

def add(a,b):
    return a+b

# 这段代码应该只有直接运行这个文件进行测试时才要执行
# 如果别的代码导入本模块,这段代码不应该被执行
ret = add(12,22)
print('测试的结果是',ret)

demo.py

import test1.py   # 只要导入了tets1.py,就会立刻执行 test1.py 代码,打印测试内容

为了解决这个问题,python在执行一个文件时有个变量__name__.在Python中,当直接运行一个py文件时,这个py文件里的__name__值是__main__,据此可以判断一个一个py文件是被直接执行还是以模块的形式被导入。

def add(a,b):
    return a+b

if __name__ == '__main__':  # 只有直接执行这个py文件时,__name__的值才是 __main__
    # 以下代码只有直接运行这个文件才会执行,如果是文件被别的代码导入,下面的代码不会执行
    ret = add(12,22)
    print('测试的结果是',ret)

在当前模块中执行的时候,__name__的值是__main__。
而在导入该模块的文件中运行的时候,该模块的__name__的值是该模块名
所以加上__name__ == 'main’这样一个判断语句,可以保证测试代码只在该模块自己执行的时候执行

pycharm中直接输入mian,就会自动生成这句话

自定义模块

除了使用系统提供的内置模块以外,我们还能自己写一个模块供自己的程序使用。一个py文件就是一个模块,所以,自定义模块很简单,基本上相当于创建一个py文件。但是,需要注意的是,如果一个py文件要作为一个模块被别的代码使用,这个py文件的名字一定要遵守标识符的命名规则

使用from <模块名> import *导入一个模块里所有的内容时,本质上是去查找这个模块的__all__属性,将__all__属性里声明的所有内容导入。如果这个模块里没有设置__all__属性,此时才会导入这个模块里的所有内容。

模块里以一个下划线_开始的变量和函数,是模块里的私有成员,当模块被导入时,以_开头的变量默认不会被导入。但是它不具有强制性,如果一个代码强行使用以_开头的变量,有时也可以。但是强烈不建议这样使用,因为有可能会出问题。

看下面例子:
test1.py:模块里没有__all__属性

a = 'hello'
def fn():
    print('我是test1模块里的fn函数')

test2.py:模块里有__all__属性,规定了哪些能够被import * 导入(只是*不能导入,但是可以单独导入其他内容)

x = '你好'
y = 'good'
def foo():
    print('我是test2模块里的foo函数')
__all__ = ('x','foo')

test3.py:模块里有以_开头的属性,即私有属性

m = '早上好'
_n = '下午好'
def _bar():
    print('我是test3里的bar函数')

导入并调用

from test1 import *
from test2 import *
from test3 import *

print(a)
fn()

print(x)
# print(y) 会报错,test2的__all__里没有变量 y
foo()


print(m)
# print(_n)  会报错,导入test3时, _n 不会被导入

import test3
print(test3._n)  # 也可以强行使用,但是强烈不建议

在自定义模块时,需要注意一点,自定义模块名不要和系统的模块名重名,否则会出现问题!

一个模块就是一个 py 文件,在 Python 里为了对模块分类管理,就需要划分不同的文件夹。多个有联系的模块可以将其放到同一个文件夹下,为了称呼方便,一般把 Python 里的一个代码文件夹称为一个包。

包和文件夹的区别,可以看到创建包以后里面会自带一个init文件,但是文件里没有任何内容
文件夹里一般放的都是非python文件,包里面放的是py文件
在文件夹里面创建一个__init__的py文件,也就变成包了

包的命名也要遵循规则,只能出现字母数字下划线

导入包

  1. 直接使用包名.模块模块名导入指定的模块。
  2. 使用from xxx import xxx 方式导入指定模块。
  3. 使用from 包名.模块名 import 类/函数 导入指定包里的模块中的类或者函数
  4. from 包名.模块名 import * 导入模块下的内容
  5. from .模块 import 类/函数 . 表示当前包下,和相对路径差不多,有时候会出问题
  6. from 包名 import * 只会导入__init__文件中允许访问的模块,如果没有写__all__= [],那么都不能访问,一般不会这样导入包

__init__文件

导入包的时候,默认调用__init__.py文件,执行文件中的内容,加载文件中的函数
调用这个文件中的内容,不需要加模块名(__init__),直接包名加函数名或者类名就可以了

__init__.py 控制着包的导入行为。__init__.py为空仅仅是把这个包导入,不会导入包中的模块。可以在__init__.py文件中编写内容。

可以在newmsg里创建__init__.py文件,在该文件里导入指定的内容。

在__init__.py文件里编写代码:

from . import sendmsg  # 导入指定的模块    . 代表的是当前文件夹

test.py文件里的代码

import newmsg # 导入时,只需要输入包名即可。在包名的__init__.py文件里,导入了指定模块
newmsg.sendmsg.sendm_msg()  # 可以直接调用对应的方法
# newmsg.recvmsg.recv_msg()   不可以使用 recvmsg 模块,因为 __init__.py文件里没有导入这个模块

也可以使用__init__.py文件,结合__all__属性,导入包里的所有模块。

在newmsg包里的__init__.py文件里编写代码:

__all__ = ["sendmsg","recvmsg"]  # 指定导入的内容

test.py文件代码:

from newmsg import *  # 将newmsg里的__inint__.py文件里,__all__属性对应的所有模块都导入
sendmsg.sendmsg()
recvmsg.recvmsg()

循环导入出现的问题

A模块需要导入B模块,B模块需要导入A模块

执行A的时候需要导入B,而在导入B的时候又需要导入A,那么就会导致循环导入,而出错

那么如何解决呢?
1.重新定义一下架构
2.将导入的语句放在函数里面
3.把导入语句放在模块的最后

sys模块

表示python自身的运行环境

import sys
sys.path # 模块的查找路径
sys.version # 版本
sys.argv # 传递给Python脚本的命令行参数列表,运行程序时的参数
sys.exit(code) # 让程序以指定的退出码结束

sys.stdin # 标准输入。可以通过它来获取用户的输入
sys.stdout # 标准输出。可以通过修改它来百变默认输出
sys.stderr # 错误输出。可以通过修改它来改变错误删除

sys.argv # 传递给Python脚本的命令行参数列表,运行程序时的参数
就和java中,给main函数传递的args一样。

print(sys.argv)   # ['C:/start/包.py', '100', 'hello']     

time模块

time模块不仅可以用来显示时间,还可以控制程序,让程序暂停(使用sleep函数)
重点time.time()、sleep()、strftime()

print(time.time())  # 获取从1970-01-01 00:00:00 UTC 到现在时间的秒数
print(time.strftime("%Y-%m-%d %H:%M:%S")) # 按照指定格式输出时间
print(time.asctime()) #Mon Apr 15 20:03:23 2019   #
print(time.ctime()) # Mon Apr 15 20:03:23 2019    # # 将时间戳转成字符串

t = time.time()
print(time.ctime(t)) # 将时间戳转成字符串
print(time.localtime(t))  # 将时间戳转成元组的形式
# time.struct_time(tm_year=2022, tm_mon=7, tm_mday=14, tm_hour=18, tm_min=26, tm_sec=44, tm_wday=3, tm_yday=195, tm_isdst=0)
print(time.localtime(t).tm_hour)  # 18
print(time.mktime(time.localtime(t))) # 1657794504.0  将元组转成时间戳

print(time.strptime('2022/7/14', '%Y/%m/%d'))  # 将字符串的时间转换成元组形式
# time.struct_time(tm_year=2022, tm_mon=7, tm_mday=14, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=195, tm_isdst=-1)
print('hello')
print(time.sleep(3)) # 让线程暂停10秒钟
print('world')

datatime模块

datetime模块主要用来显示日期时间,这里主要涉及 date类,用来显示日期;time类,用来显示时间;datetime类,用来显示日期时间;timedelta类用来计算时间差。

import datetime

print(datetime.date(2020, 1, 1))  # 创建一个日期
print(datetime.time(18, 23, 45)) # 创建一个时间
print(datetime.datetime.now())  # 获取当前的日期时间
print(datetime.datetime.now() + datetime.timedelta(3))  # 计算三天以后的日期时间

random模块

random 模块主要用于生成随机数或者从一个列表里随机获取数据。

print(random.random())  # 生成 [0,1)的随机浮点数
print(random.uniform(20, 30))  # 生成[20,30]的随机浮点数
print(random.randint(10, 30))  # 生成[10,30]的随机整数,包含结尾
print(random.randrange(20, 30))  # 生成[20,30)的随机整数,可以加步长,不包含结尾数
print(random.choice('abcdefg'))  # 从列表里随机取出一个元素
print(random.sample('abcdefghij', 3)) # 从列表里随机取出指定个数的元素

ll = [1,2,3,4,5]
random.shuffle(ll) # 随即打乱
print(ll)

hashlib模块

hashlib是一个提供字符加密功能的模块,包含MD5和SHA的加密算法,具体支持md5,sha1, sha224, sha256, sha384, sha512等算法。该模块在用户登录认证方面应用广泛,对文本加密也很常见。
base64这个加密算法是可逆的,提供的一般都是不可逆的

import hashlib

# 待加密信息
str = '这是一个测试'

# 创建md5对象
hl = hashlib.md5('hello'.encode(encoding='utf8'))
print('MD5加密后为 :' + hl.hexdigest())

h1 = hashlib.sha1('123456'.encode())
print(h1.hexdigest())
h2 = hashlib.sha224('123456'.encode())
print(h2.hexdigest())
h3 = hashlib.sha256('123456'.encode())
print(h3.hexdigest())
h4 = hashlib.sha384('123456'.encode())
print(h4.hexdigest())

msg = '123456'
print(hashlib.sha3_256(msg.encode('utf-8'))) # <_sha3.sha3_256 object at 0x000001C517AA6600>

第三方包

对第三方包的管理主要包含查找、安装和卸载三个部分的操作。

使用 pip install <包名>命令可以安装指定的第三方资源包。

pip install ipython # 安装ipython包

使用 install 命令下载第三方资源包时,默认是从 pythonhosted下载,由于各种原因,在国内下载速度相对来说比较慢,在某些时候甚至会出现连接超时的情况,我们可以使用国内镜像来提高下载速度。

如果只是想临时修改某个第三方资源包的下载地址,在第三方包名后面添加 -i 参数,再指定下载路径即可,格式为pip install <包名> -i <国内镜像路径>

pip install ipython -i https://pypi.douban.com/simple

除了临时修改pip的下载源以外,我们还能永久改变pip的默认下载路径。

在当前用户目录下创建一个pip的文件夹,然后再在文件夹里创建pip.ini文件并输入一下内容:

[global]
index-url=https://pypi.douban.com/simple
[install]
trusted-host=pypi.douban.com

常见国内镜像

阿里云 https://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) https://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 https://pypi.mirrors.ustc.edu.cn/simple/

卸载

使用 pip install <包名>命令可以用来卸载指定的第三方资源包。

以上是关于Python学习6(单例模式模块包)的主要内容,如果未能解决你的问题,请参考以下文章

python3.6_单例模式

Python基础入门学—单例异常模块和包

Python3.6单例模式报错TypeError: object() takes no parameters的解决方法

Python中的单例模式

python Logger模块单例模式

Python设计模式之单例模式