列表推导式和生成器,单例。

python3中列表推导式和生成器的不同:

(1)列表推导式是将所有的值一次性加载到内存中

生成器是将列表推导式的[]改成(),不会将所有的值一次性加载到内存中,延迟计算,一次返回一个结果,它不会一次生成所有的结果,这对大数据量处理,非常有用

def fun():
    for i in range(1,50):
        sleep(1)
        yield i

for i in fun():
    print(i)
# 生成器函数: 一个函数中包含了yield关键,那么这个函数就不是普通的函数,是一个生成器函数
# 调用生成器函数,不会立马执行该函数里面的代码, 而是会返回一个 生成器
def func():
    print("a")
    yield
    print("b")
    yield
    print("c")
    yield
    print("d")
    yield
generator = func()
print(generator)
print(type(generator))

# next(generator)
# next(generator)
# next(generator)
for i in generator:
    pass

sum(x for x in range(10000000000))
sum([x for x in range(10000000000)])

第一个几乎没什么内存占用,第二个内存占有很多

原理:sum函数时python3中的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以我们可以直接计算一系列值的和,而不用多此一举先构造一个列表.

生成器还可以提高代码的可读性:

# 求一段文字中,每个单词出现的位置
def index_words(text):
    result = []
    if text:
        result.append(0)
    for index,letter in enumerate(text,1):
        if letter == ' ':
            result.append(index)
    return result

def index_words(text):
    if text:
        yield 0
    for index,letter in enumerate(text,1):
        if letter == '':
            yield index

(2)列表推导式可以遍历任意次

生成器只能遍历一次

generator = (i for i in range(1,5))
print(next(generator))
print(next(generator))
for i in generator:
    print(i)
for i in generator:
    print(i)
打印结果:
1
2
3
4

list1 = [i for i in range(1,5)]
print(list1[0])
print(list1[1])
print(list1[2])
for i in list1:
    print(i)
for i in list1:
    print(i)
打印结果:
1
2
3
1
2
3
4
1
2
3
4

python__new__方法以及与装饰器单例模式比较

在学习面向对象的时候,我们接触过init方法,它是来初始化类的属性的,那么new方法是干什么的呢?

1.new方法的定义

new方法是来实例化类的对象的,它返回类的对象(对的常见的obj = cls()就是调用new来返回实例)所以它在init方法前调用,因为在调用init方法时,类的实例化对象已经生成了 
通过下面的例子来理解:

class Foo():

    def __init__(self):
        print("This is init func")

    def __new__(cls, *args, **kwargs):
        print("This is new func")
        # return super(Foo, cls).__new__(cls) #返回类的实例化对象
        # return object.__new__(cls) #同上


obj1 = Foo()

执行结果为 
This is new func 
但是,init方法并没有执行啊?因为我们只是调用了new方法,但是并没有返回实例,这个例子只是体现出new方法在init之前,我们取消new方法的注释,随便用哪个都行,这两个return都是返回类的实例

class Foo():

    def __init__(self):
        print("This is init func")

    def __new__(cls, *args, **kwargs):
        print("This is new func")
        return super(Foo, cls).__new__(cls) #返回类的实例化对象
        # return object.__new__(cls)


obj1 = Foo()

执行结果为: 
This is new func 
This is init func 
可以看到,先用new返回实例化对象,再执行的init方法

2.使用new方法返回单例

单例在程序设计中是比较常用的设计模式,它用于类只能实例化一个对象,比如某个程序只能执行出现一个窗口,不能同时运行多个,这个大多就是通过单例模式来实现的

class Foo():

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

    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, 'instance'):  # 当对象不存在instance属性,即类还没有被实例化时,返回实例化对象
            cls.instance = super(Foo, cls).__new__(cls)  # 创建当前类的对象,相当于object.__new__(cls)
        return cls.instance


obj1 = Foo("123")
print(obj1.name)
obj2 = Foo("321")
print(obj1 == obj2, obj1.name, obj2.name, id(obj1), id(obj2))

执行结果为: 
123 
True 321 321 2239300270792 2239300270792

这里的核心思想是,定义instance属性(随便什么名都行,代表这个属性是类的实例化对象),判断这个属性是否存在,若不存在,则创建实例对象,若存在则直接返回实例 
而且我们可以看到,当我们重新实例化这个对象时,其实这个对象的id没有变化,但是属性改变了,记住这点,我们接下来会与装饰器单例进行比较

3.装饰器单例模式

既然都说到单例模式了,不得不提的是装饰器的单例模式,那么装饰器单例的实现方式如下列代码:

def decorator_single_obj(cls, *args, **kwargs):
    instance = {}  # 创建字典来保存实例

    def get_instance(*args, **kwargs):
        if cls not in instance:  # 若实例不存在则新建
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]

    return get_instance

@decorator_single_obj
class Foo(object):
    def __init__(self, name):
        self.name = name


obj1 = Foo("123")
print(obj1.name, id(obj1))
obj2 = Foo("321")
print(obj2.name, id(obj2))

我们可以看到,虽然也还是实现了单例模式,但是第二次实例化得时候,属性并没有被改变 
所以装饰器单例与new单例的区别在于属性不会被覆盖 
因为new会重新调用init方法,为实例重新初始化属性,而装饰器单例模式则是直接返回之前生成的对象,并不会重新初始化对象,所以new单例从某种程度上也可以视作伪单例模式

最后呢,再留个彩蛋 
对于上面的代码我们加上一行 
obj3=object.__new__(Foo) 
会发生什么?

链接:
https://blog.youkuaiyun.com/renyiforever/article/details/79859394
https://blog.youkuaiyun.com/jimmy_gyn/article/details/79142101


 

列表推导式和生成器,单例。

识别图中二维码,欢迎关注python宝典

帮我把下面的面试提问进行回答,然后用简洁的方式整理成简历上的技术总结(一共需要15行):以下是Python高级开发工程师面试中常问的问题总结: ### 语言特性 1. **装饰器** - 解释装饰器的原理用途。如如何实现一个简的日志装饰器。 - 装饰器如何处理带参数的函数类方法。 - 内置装饰器`@staticmethod`、`@classmethod``@property`的区别使用场景。 2. **迭代器生成器** - 阐述迭代器生成器的概念、区别及应用场景。 - 如何手动实现一个迭代器生成器。 - 生成器表达式列表推导式的对比,以及它们在内存使用上的差异。 3. **元类** - 解释元类的概念作用。 - 什么时候需要使用元类,举说明。 - 如何使用元类来实现模式。 4. **上下文管理器** - 解释上下文管理器的工作原理使用场景。 - 如何使用`__enter__``__exit__`方法手动实现一个上下文管理器。 - 如何使用`contextlib.contextmanager`装饰器创建上下文管理器。 ### 面向对象编程 1. **继承多态** - 解释继承多态的概念,并举说明。 - Python中如何实现多重继承,以及可能会遇到的问题解决方案(如菱形继承`MRO`)。 - 如何在子类中调用父类的方法。 2. **类属性属性** - 解释类属性属性的区别使用场景。 - 当类属性属性同名时,访问规则是怎样的。 3. **魔法方法** - 列举一些常用的魔法方法(如`__init__`、`__str__`、`__repr__`等),并说明它们的作用。 - 如何实现自定义的比较运算符(如`__eq__`、`__lt__`等)。 ### 性能优化 1. **内存管理** - 解释Python的内存管理机制,包括引用计数、垃圾回收内存池。 - 如何使用`sys.getsizeof``memory_profiler`等工具来分析对象的内存使用情况。 - 列举一些减少内存使用的技巧,如使用生成器、减少全局变量等。 2. **性能分析优化** - 如何使用`cProfile``line_profiler`等工具来分析代码的性能瓶颈。 - 举说明如何优化循环、递归等代码结构。 - 解释Python的GIL(全局解释器锁),以及它对多线程性能的影响解决方案。 ### 并发编程 1. **多线程多进程** - 解释多线程多进程的概念、区别使用场景。 - 如何使用`threading``multiprocessing`模块来创建管理线程进程。 - 如何处理多线程多进程中的共享资源同步问题,如使用锁、信号量等。 2. **异步编程** - 解释异步编程的概念优势。 - 如何使用`asyncio`库来实现异步编程,包括协程、事件循环等。 - 异步编程多线程、多进程的对比,以及它们的适用场景。 ### 数据库网络编程 1. **数据库操作** - 如何使用`SQLAlchemy`等ORM(对象关系映射)库来操作数据库。 - 解释数据库事务的概念实现方式。 - 如何优化数据库查询性能,如索引的使用、查询优化等。 2. **网络编程** - 如何使用`socket`模块来实现简的网络通信。 - 解释HTTP协议的基本原理工作流程。 - 如何使用`requests`库来发送HTTP请求,以及如何处理响应。 ### 设计模式代码规范 1. **设计模式** - 列举一些常见的设计模式(如模式、工厂模式、观察者模式等),并说明它们的应用场景。 - 如何使用Python实现某个设计模式。 2. **代码规范** - 解释PEP 8代码规范的主要内容重要性。 - 如何使用`pylint``flake8`等工具来检查代码的规范性。
04-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值