小鱼牌豆腐饼

小鱼牌豆腐饼 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

小鱼牌豆腐饼 赞了文章 · 2月19日

单例模式的五种设计方案

python设计模式之单例模式

单例模式是一种常用的开发设计模式,它的主要目的就是确保只有一个实例对象的存在,也就是说当你希望一个类的功能比较单一,你只需要一个实例对象就可以完成需要的时候,就可以使用单例模式,以此来节省内存资源。

比如我们在web开发项目中,我们经常需要做到的一个功能就是使用单例模式来开发短信验证码这个功能。我们通常是使用通讯产品来发送短信验证码,那么我们就只需要用一个实例对象去完成这个短信发送的功能就可以了。

1. 模块实现单例模式

大家应该都知道,模块的导入只能够让被导入程序执行一次,你多次导入也只会执行一次,那么我们可以说模块就是一个天然的单例模式,因此,我们只需把相关函数和数据定义在一个模块中,使用的时候将它导入到其他模块使用就可以达到单例模式的效果了。

single.py
···
class Singleton(object):
    def foo(self):
        pass
    print(1)
singleton = Singleton()

from singleton import singleton
from singleton import singleton
执行结果:
1 

2. 使用装饰器实现单例模式

装饰器的作用相信大家都是知道的,可以给我们别的函数或者类添加一个功能,那么我们同样的可以给类写一个逻辑,让类只能生成一个实例对象。

def Singleton(cls):
    _instance = {}
    def singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return singleton

@Singleton
class A(object):
    a = 1
    def __init__(self, x=0):
        self.x = x

a1 = A(2)
a2 = A(3)
print(a1)
print(a2)
执行结果:
<__main__.A object at 0x0000027432DABE48>
<__main__.A object at 0x0000027432DABE48> 

3. 使用类方法实现单例模式

当我们实用类直接创建实例对象的时候,创建的并不是单例对象,那么我们需要在类中定义一个类方法来编辑逻辑实现单例模式,主要思路就是会去判断类是否有_instance这个属性,如果有则直接返回这个实例,没有则创建实例。

class Singleton(object):
    def __init__(self,*args,**kwargs):
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

s1 = Singleton.get_instance()
s2 = Singleton.get_instance()
print(s1)
print(s2)
执行结果:
<__main__.Singleton object at 0x0000017B0359B550>
<__main__.Singleton object at 0x0000017B0359B550> 

但是这样去实现单例模式有个隐患,当我们加入多线程去创建实例对象的时候,我们的执行速度够快还不会出现影响,当执行速度不够快的时候,一个线程去创建实例然后拿到了_instance这个属性去判断,其他的线程可能也同时会拿到这个_instance这个属性,发现并没有实例存在,所以这两个线程就会同时创建一个实例,就会造成创建了多个实例的现象,我们的单例模式自然也就失效了。举例来说明,我们这里直接加上这个休息时间演示执行慢的情况。

import threading
import time

class Singleton(object):
    def __init__(self, *args, **kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
执行结果:
<__main__.Singleton object at 0x000001774942A518>
<__main__.Singleton object at 0x00000177495FA160>
<__main__.Singleton object at 0x00000177495F1EF0>
<__main__.Singleton object at 0x00000177495E5940>
<__main__.Singleton object at 0x00000177495AC0F0>
<__main__.Singleton object at 0x00000177495E58D0>
<__main__.Singleton object at 0x00000177495A6FD0>
<__main__.Singleton object at 0x00000177495F1D68>
<__main__.Singleton object at 0x00000177496060B8>
<__main__.Singleton object at 0x0000017749606240> 

现在我们是不是就发现了这个实例对象变为了不同的ip了?这个也就是我们刚才所说道的这个原因。那么我们怎么去解决这问题呢?在这里我们可以通过加上线程锁达到效果。我们加了线程锁之后,给这个_instance加上这个锁,让每一个县城用完了之后才释放它,接着下一个线程才能拿到它继续运行程序,就不会再造成同时拿到这个属性值而直接创建了不同的实例的情况。

import threading
import time

class Singleton(object):
    _instance_lock = threading.Lock()
    def __init__(self, *args, **kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls, *args, **kwargs):
        if not hasattr(Singleton, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(Singleton, '_instance'):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
执行结果:
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550>
<__main__.Singleton object at 0x000001EC8FAB9550> 

4. 使用__new__方法实现单例模式

大家应该都知道我们的实例对象就是通过__new__方法创建出来的,如果我们重写类的__new__方法,我们是不是可以直接把这个可以创建多个实例的情况改写为只能创建一个实例对象呢?答案是肯定的,我们可以在重写__new__方法的时候,添加判断如果存在了实例对象,那么直接返回已经创建了的对象即可,这样就可以达到我们的这个效果了。

import threading

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self, *args, **kwargs):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            with Singleton._instance_lock:
                if not hasattr(cls, '_instance'):
                    Singleton._instance = super().__new__(cls)

            return Singleton._instance

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task, args=[i, ])
    t.start()
运行结果:
<__main__.Singleton object at 0x00000111C88B9588>
None
None
None
None
None
None
None
None
None 

5. 使用元类metaclass实现单例模式

Python的元类是控制类的创建的类,既然如此,那么我们是不是可以再在创建类的时候通过元类来创建我们的类,使得它只能生成一个实例对象呢?

import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name

obj1 = Foo('name')
obj2 = Foo('name')
print(id(obj1))
print(id(obj2))
执行结果:
<__main__.Foo object at 0x000001E97FC14400>
<__main__.Foo object at 0x000001E97FC14400> 

好了,那么这里就是给大家介绍的几种创建单例模式的方法,总的来说,不管有没有用,面试的时候,我们装x还是有作用的,你get到了吗?

查看原文

赞 1 收藏 0 评论 0

小鱼牌豆腐饼 回答了问题 · 2月8日

python 在windows 可以找到这个utils package ,但是在linux 报错如下,求问

Pycharm 设置pythonPath的环境变量douyin,也就是项目根目录 ,这个目录下的所有 python package 都可以被直接import

但是linux下没有pythonPath, 所以无法引入

关注 3 回答 2

小鱼牌豆腐饼 回答了问题 · 2月8日

为何subprocess调用的py中的raise没起作用?

因为子进程引发异常 不会影响主进程

关注 2 回答 2

小鱼牌豆腐饼 关注了用户 · 1月7日

豌豆花下猫 @chinesehuazhou

pythonista,公众号:Python猫

关注 49

小鱼牌豆腐饼 赞了回答 · 2020-12-17

解决这段Python代码怎么优化?

如果是我,应该会这么写:

originData = { 'a': None, 'b': None, 'c': 5, 'list': [{ 'a': 1, 'b': None}], 'dict': { 'a': 1, 'b':None}}

def remove_none(data):
    if isinstance(data, dict):
        return {
            k: remove_none(v)
            for k, v in data.items()
            if v is not None
        }
    elif isinstance(data, list):
        return [
            remove_none(v)
            for v in data
            if v is not None
        ]
    else:
        return data

print(remove_none(originData))

不做就地修改,利用字典和列表推荐简化程序处理逻辑。

想知道编写优雅Python代码的秘密?请关注我的专栏:Python源码深度剖析

关注 3 回答 2

小鱼牌豆腐饼 回答了问题 · 2020-12-17

在中文段落中使用python的re模块,怎样在表达式中写不匹配“不”字

[^abc]

关注 2 回答 1

小鱼牌豆腐饼 回答了问题 · 2020-11-24

解决这段Python代码怎么优化?

首先不能在 循环时 对列表进行remove

关注 3 回答 2

小鱼牌豆腐饼 回答了问题 · 2020-11-13

python书写规范的问题

我觉得链式调用更合理一些。前提是objy不会再次复用

关注 2 回答 1

小鱼牌豆腐饼 赞了文章 · 2020-11-03

Python 代码重构:使用 any()和 all() 重构 for 循环

问题描述

使用 for 迭代一个可迭代对象,对每个对象进行条件判断,比如找到任何一个符合条件的元素,就返回 True,否则返回False。这种操作经常用于找符合条件的元素。

代码片段 例1

def is_user_exist(name: str) -> bool:
    for user in users:
        if user['user'] == name:
            return True
    return False

运行:判断用户是否存在

In [33]: users = [{'user':'john','password':'mypassword'}, {'user':'william', 'password':'yourpass'}]
In [38]: is_user_exist('john')
Out[38]: True

In [39]: is_user_exist('johny')
Out[39]: False

重构

使用 any(iterable)重构

def is_user_exist_v2(name: str) -> bool:
   return any(user['user'] == name for user in users)

运行

In [43]: is_user_exist_v2('john')
Out[43]: True

In [44]: is_user_exist_v2('johnny')
Out[44]: False

In [45]:

代码片段 例 2

from typing import List, Dict
def all_user_has_password(usergroup: List[Dict]) -> bool:
    for user in usergroup:
        if user['password'] == '':
            return False
    return True

运行:判断用户组是否都设置了密码

In [49]: users_group1 = [{'user':'john','password':'mypassword'}, {'user':'william', 'password':'yourpass'}]

In [50]: users_group2 = [{'user':'john','password':'mypassword'}, {'user':'william', 'password':'yourpass'}, {'user': 'user1', 'password':''}]

In [58]: all_user_has_password(users_group1)
Out[58]: True

In [59]: all_user_has_password(users_group2)
Out[59]: False

重构

使用 all(iterable) 进行重构

def all_user_has_password_v2(usergroup: List[Dict]) -> bool:
    return all(user['password'] != '' for user in usergroup)

运行

In [61]: all_user_has_password_v2(users_group1)
Out[61]: True

In [62]: all_user_has_password_v2(users_group2)
Out[62]: False

总结

适当地使用 all()any() 可以有效减少代码的复杂度,能够很容易的对一个可迭代对象进行条件判断。

注,v2 的后缀是个非常不好的命名,在这里使用仅仅为了区分两个函数,不应该在代码中这样命名

参考

Built-in Functions — Python 3.7.7 documentation

查看原文

赞 1 收藏 0 评论 0

小鱼牌豆腐饼 回答了问题 · 2020-09-23

我用html/js/css做一些和地图相关的可视化,其他同事用的python在本地跑一些算法,我怎么样从前端获取他的结果呢?

在线模型的话,让算法封装接口。
离线的算法模型,把结果落数据库吧,封装接口查询数据库。

关注 3 回答 1

认证与成就

  • 获得 2 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-10-26
个人主页被 468 人浏览