1、 _ _ getattr _ _与 _ _ getattribute _ _
- _ _ getattr _ _ 是当类调用一个不存在的属性时才会调用getattr魔法函数,他传入的值item就是你这个调用的不存在的值,如果传入的是已有的属性,那么就会去寻找已有的属性。
- _ _ 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、属性描述符
- 属性表述符协议
属性描述符是实现了特定协议的类,只要实现了__get__,__set__和__delete__三个方法中的任意一个,这个类就是描述符,它能实现对多个属性运用相同存取逻辑的一种方式,通俗来说就是:创建一个实例,作为另一个类的类属性。
2.如果一个对象同时定义了__get__和__set__方法,它被称做数据描述符(data descriptor)。
3.只定义__get__方法的对象则被称为非数据描述符(non-data descriptor)。
3、元类
元类就是创建类的类可以用两种方法创建,第一种是type,第二种是通过classmate传入参数
type()一共要传入三个参数
- 第一个参数:name表示类名称,字符串类型
- 第二个参数:bases表示继承对象(父类),元组类型,单元素使用逗号
- 第三个参数: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迭代器
- 有内置的 _ _ next _ _ ()方法的对象,执行该方法可以不依赖索引取值,有内置的 _ _ iter _ _ ()方法的对象,执行迭代器的 _ _ iter _ _ ()方法得到的依然是迭代器本身
- 可以被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可迭代对象与迭代器区别
- 可用于for循环的都是可迭代对象
- 作用于next()都是迭代器
- 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关键字以及将列表推导式的[ ]写成括号
- 生成器表达式
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)
- 生成器函数
通过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)