python进阶:python高级编程技巧(下)

1、 _ _ getattr _ _与 _ _ getattribute _ _

  1. _ _ getattr _ _ 是当类调用一个不存在的属性时才会调用getattr魔法函数,他传入的值item就是你这个调用的不存在的值,如果传入的是已有的属性,那么就会去寻找已有的属性。
  2. _ _ getattribute _ _则是无条件的优先执行,所以如果不是特殊情况最好不要用 _ _ getattribute _ _。
class User(object):
    def __init__(self, name, info):
        self.name = name
        self.info = info

    def __getattr__(self, item):
        return self.info[item]

    # def __getattribute__(self, item):
    #     return 'hhh'
    # 如果写了这个方法,那么所有的属性调用都会返回hhh


ls = User("李四", {"gender": "male"})
print(ls.gender)
print(ls.name)

2、属性描述符

  1. 属性表述符协议
    属性描述符是实现了特定协议的类,只要实现了__get__,__set__和__delete__三个方法中的任意一个,这个类就是描述符,它能实现对多个属性运用相同存取逻辑的一种方式,通俗来说就是:创建一个实例,作为另一个类的类属性。
    2.如果一个对象同时定义了__get__和__set__方法,它被称做数据描述符(data descriptor)。
    3.只定义__get__方法的对象则被称为非数据描述符(non-data descriptor)。

3、元类

元类就是创建类的类可以用两种方法创建,第一种是type,第二种是通过classmate传入参数
type()一共要传入三个参数

  1. 第一个参数:name表示类名称,字符串类型
  2. 第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
  3. 第三个参数:attr表示属性,这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值
def __init__(self, name):
    self.name = name
    print("i am __init__")
    
    
User = type("User", (), {"age":22 , "__init__":__init__})
obj = User("beiyue")       
print(obj.name)       

metaclass的英文直译过来就是元类,支持自己定义一个类,使得它是后面某一个类的元类。

class AddInfo(type):
    def __new__(cls, name, bases, attr):
        attr['info'] = 'add by metaclass'
        return super().__new__(cls, name, bases, attr)
        
        
class Test(metaclass=AddInfo):
    pass

4、迭代器与可迭代对象

4.1可迭代Iterable

可以用for循环遍历的对象都是可迭代对象。
str,list,tuple,dict,set等都是可迭代对象。
generator,包括生成器和带yield的生成器函数。

4.2判断一个对象是否可迭代

除了看内置是否含有 _ _ iter _ _ 方法来判断该对象是否是一个可迭代的对象之外,我们还可以使用 isinstance() 判断一个对象是否是 Iterable 对象

from collections import Iterable,Iterator
print(isinstance('abc',Iterable))   # True
print(isinstance([1,2,3,4],Iterable))   # True
print(isinstance(123,Iterable))     # False

4.3迭代器

  1. 有内置的 _ _ next _ _ ()方法的对象,执行该方法可以不依赖索引取值,有内置的 _ _ iter _ _ ()方法的对象,执行迭代器的 _ _ iter _ _ ()方法得到的依然是迭代器本身
  2. 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以通过iter()方法将可迭代的对象,转为迭代器。
from collections import Iterable,Tterator
li = [1,2,3,4]
print(isinstance(li,Iterable)) # true
print(isinstance(li,Iterable)) # false
lis = iter(li)
print(type(lis))    # <class 'list_iterator'>

4.4for循环的原理

d = {'name': 'beiyue', 'city': 'sh', 'skill': 'python'}
for i in d:
	print(i)

for 循环过程中,可迭代对象 d 会先通过 iter() 生成一个迭代器 iter(d), 该迭代器具有一个 next 方法。在每次迭代过程中,这个 next 方法会被调用,从而返回 d 中的下一个键,直至字典的键全部返回后,next 会引发 StopIteration 异常,而 for 循环则会通过捕捉该异常来终止迭代,python的for循环本质就是通过不断调用next()函数实现的

4.5可迭代对象与迭代器区别

  1. 可用于for循环的都是可迭代对象
  2. 作用于next()都是迭代器
  3. list、dict、str等都是可迭代的但不是迭代器,因为next()函数无法调用它们。可以通过iter()函数将它们转为迭代器

5、生成器

5.1效果说明

在Python中,一边循环一边计算的机制,称为生成器:generator。
对比生成器和迭代器所占有的内存

import os
import psutil


# 显示当前 python 程序占用的内存大小
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used: {} MB'.format(hint, memory))


def test_iterator():
    show_memory_info('initing iterator')
    list_1 = [i for i in range(1000000)]
    print(list_1)
    show_memory_info('after iterator initiated')
    print(sum(list_1))
    show_memory_info('after sum called')


def test_generator():
    show_memory_info('initing generator')
    list_2 = (i for i in range(1000000))
    print(list_2)
    show_memory_info('after generator initiated')
    print(sum(list_2))
    show_memory_info('after sum called')


test_iterator()
test_generator()

生成器并不会像迭代器一样占用大量内存,只有在被使用的时候才会调用。而且生成器在初始化的时候,并不需要运行一次生成操作,相比于 test_iterator() ,test_generator() 函数节省了一次生成一亿个元素的过程,因此耗时明显比迭代器短。

5.2为什么要有生成器

列表所有数据都在内存中,如果有海量数据的话会非常消耗内存。
比如说:我们仅仅需要访问前面几个元素,但后面绝大多元素占用的内存就会浪费了,那么生成器就是在循环的过程中根据算法不断推算出后续的元素,这样就不用创建整个完整的列表,从而节省大量的空间,总而言之,就是当我们想要使用庞大数据,又想让它占用的空间少,那就使用生成器。

5.3如何创建生成器

一共两种方法,yield关键字以及将列表推导式的[ ]写成括号

  1. 生成器表达式
g = (x for x in range(5))
print(g)       # generator object
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# 超出报错
print(next(g))
for i in g:
    print(i)
  1. 生成器函数
    通过yield返回一个值,并且记住这个返回值的位置,下次遇到next()调用时,代码从yield的下一条语句开始执行。与return的差别是,return也是返回一个值,但是直接结束函数。
def createNums():
    print("-----func start-----")
    a,b = 0,1
    for i in range(5):
        # print(b)
        print("--1--")
        yield b
        print("--2--")
        a,b = b,a+b
        print("--3--")
    print("-----func end-----")
    
g = createNums()
print(next(g))  
print(next(g))  
print(next(g))
print(next(g))
print(next(g))

5.4通过生成器读取大文件

"""文件样式为一行以|分割"""
def readlines(f,newline):
    buf = ""
    while True:
        while newline in buf:
            pos = buf.index(newline)
            yield buf[:pos]
            buf = buf[pos + len(newline):]
        chunk = f.read(4096*10)
        if not chunk:
            yield buf
            break
        buf += chunk
with open('demo.txt') as f:
    for line in readlines(f,"{|}"):
        print(line)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值