1、元类编程
__getattr__和__getattribute__魔法函数
from datetime import date, datetime
class User:
def __init__(self, name, birthday, info={}):
self.name = name
self.birthday = birthday
self.info = info
def __getattr__(self, item): # 当类对象的属性不存在的时候,会调用此方法
print("not find attr")
# return self.info[item]
return self.info.get(item)
# def __getattribute__(self, item): # 优先级比__getattr__高,一般不重写
# return '2'
if __name__ == "__main__":
user = User("1", date(year=1990, month=1, day=1), info={'age': 18})
print(user.age)
print(user.name)
2、属性描述符
如果类中有多个属性都需要判断是否存在,那么就需要写多个方法,这些方法怎么复用呢?这个时候就要用到属性描述符
属性描述符:只要实现了__get__,set,__delete__任何一个方法,就被称为属性描述符
'''
class User:
def __init__(self, age):
self.age = age
def get_age(self):
return (str(self.age) + '岁')
def set_age(self, age):
if not isinstance(age, int):
raise TypeError('Type Error')
self.age = age
'''
# 属性描述符
# __get__ __set__ __delete__ 三个方法
class IntField(object):
"""
数据描述符
"""
def __get__(self, instance, owner):
print("__get__")
print(instance) # <__main__.User object at 0x00000275DE456FD0>
print(owner) # <class '__main__.User'>
return self.values # 30
def __set__(self, instance, value):
print("__set__")
print(instance) # <__main__.User object at 0x0000025E04468320>
print(value) # 30
if not isinstance(value, int):
raise ValueError('Value Error')
self.values = value
def __delete__(self, instance):
print("__delete__")
class User:
age = IntField() # 只要是整数类型的,都可以调用这个类来判断存在性和类型,其他类型也可以这样来写
user = User()
user.age = 30 # 相当于调用的是__set__方法
print(user.age) # 相当于调用的是__get__方法
属性查找顺序:
user = User(), 那么user.age 顺序如下:
1 如果"age"是出现在User或其基类的__dict__中, 且age是data descriptor,那么调用其__get__方法, 否则
2 如果"age"出现在user的__dict__中, 那么直接返回 obj.__dict__['age'],否则
3 如果"age"出现在User或其基类的__dict__中
3.1 如果age是non-data descriptor,那么调用其__get__方法, 否则
3.2 返回 __dict__['age']
4 如果User有__getattr__方法,调用__getattr__方法,否则
5 抛出AttributeError
总结:查找属性优先级顺序:
属性描述符>obj.__ dict__>基类的__dict__>调用它的__get__>__ dict__ [‘属性’]>__ getattr__
3、自定义元类
动态创建类
# 动态创建类
def create_class(name):
if name == "user":
class User(object): # 默认继承object
def __str__(self):
return "user"
return User
elif name == "student":
class Student:
def __str__(self):
return "Student"
return Student
if __name__ == "__main__":
myclass = create_class('user')
# myclass = create_class('student')
obj = myclass()
print(obj) # user
print(type(obj)) # <class '__main__.create_class.<locals>.User'>
使用type创建类
type还可以动态的创建类,type(类名,由父类组成的元组,包含属性的字典)
# type的两种用法
"""
type(object_or_name, bases, dict)
type(object) -> the object's type 1、查看数据类型的方法
type(name, bases, dict) -> a new type 2、创建一个新的类的类
"""
# 测试第二种功能
# 创建一个新的类
user = type('User', (), {})
obj = user()
print(obj) # <__main__.User object at 0x000001D32168B198>
# 添加属性
user = type('User', (), {'name': '1', 'age': 18})
obj = user()
print(obj) # <__main__.User object at 0x000002083E328F60>
print(obj.name) # 1
print(obj.age) # 18
# 添加方法
def info(self):
return self.name
def __init__(self):
self.sex = '男'
user = type('User', (), {'name': '1', 'age': 18, "demo": info, 'sex': __init__})
obj = user()
print(obj) # <__main__.User object at 0x000002083E328F60>
print(obj.name) # 1
print(obj.age) # 18
print(obj.demo()) # 1 注意这里的方法名称和创建的时候可以不同
print(obj.sex()) # None 无法调用魔法方法__init__ 没有返回值
# 类的继承
class BaseClass(object):
def test(self):
return 'base class'
def __str__(self):
return 'this is tset'
class BaseClass1(BaseClass):
def test1(self):
return 'base class1'
user = type('User', (BaseClass, BaseClass1), {})
user = user()
print(user.test()) # base class 注意有继承查找顺序 如果方法相同,先继承BaseClass,然后查找BaseClass1
print(user.test1()) # base class1
# b = BaseClass()
# print(b) # this is tset 可以继承魔法方法
metaclass属性
如果一个类中定义了metalass = xxx,Python就会用元类的方式来创建类
# metaclass属性
# 如果一个类中定义了metalass = xxx,Python就会用元类的方式来创建类
def upper_attr(class_name, class_parents, class_attr):
# print(class_name)
# print(class_parents)
# print(class_attr)
new_attr = {} # 新建一个空字典
for n, value in class_attr.items():
pass
print(n) # name
print(value) # iam
if not n.startswith("_"): # n的起始字符如果不是'_'下划线符号
new_attr[n.upper()] = value # value值变成大写字符
# return type(class_name, class_parents, class_attr) # {'__module__': '__main__', '__qualname__': 'Foo', 'name': 'iam'}
# 元类的方式创建类,需要返回一个类 type
return type(class_name, class_parents, new_attr) # class_attr变更为new_attr
class Foo(object, metaclass=upper_attr):
# __metaclass__ = upper_attr python2的写法
name = 'iam'
f = Foo()
# print(f.name) # 当元类创建类的返回值为class_attr的时候才能用name小写
print(f.NAME) # 当返回的class_attr换成new_attr之后,需要更改属性名为大写的NAME
print(hasattr(f, 'NAME')) # True
print(hasattr(f, 'name')) # False
类的实例化过程中,首先寻找metaclass,优先级最高
# 类的实例化过程中,首先寻找metaclass,优先级最高
class Demo(object):
def __new__(cls, *args, **kwargs):
pass
class Metaclass(type):
def __new__(cls, *args, **kwargs):
pass
class User(Demo, metaclass=Metaclass):
pass
obj = User()
4、迭代器和生成器
迭代器
迭代:通过for循环遍历对象的每一个元素的过程。
Python的for语法功能非常强大,可以遍历任何可迭代的对象。
在Python中,list / tuple / string / dict / set / bytes 都是可以迭代的数据类型。
迭代器是一种可以被遍历的对象,并且能作用于next()函数。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。 迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。
from collections import Iterable, Iterator
print(isinstance(list(), Iterator)) # False 不是迭代器
print(isinstance(list(), Iterable)) # True 可以迭代
# iter next
l =[1, 2, 3, 4]
it = iter(l)
print(it)
# print(next(it))
# print(next(it))
print(next(it)) # 1
print(next(it), "_____") # 2 _____
for i in it: # 注意循环体的不同,会继续上面的next(it)的输出列表中的元素,直到遍历完成,如果next已经遍历完成,则不会继续输出,只会遍历一次
print(i) # 3 4
for i in l: # 不同于上面
print(i) # 1 2 3 4
生成器
# 斐波那契数列
def fibonacci():
print("————————functin——————")
a, b = 0, 1
for i in range(10):
# print(b)
print("---1-----")
yield b # 出现yield,就变成一个生成器了 注意输出语句顺序
print("-----2 -----")
a, b = b, a+b
print("------3 ------")
print("fun end")
g = fibonacci()
print(next(g))
print(next(g))
'''输出如下
————————functin——————
---1-----
1
-----2 -----
------3 ------
---1-----
1
'''
有时候,序列或集合内的元素的个数非常巨大,如果全制造出来并放入内存,对计算机的压力是非常大的
生成器如何读取大文件
文件300G,文件比较特殊,一行 分隔符 {|}
def readlines(f, newline):
buf = ""
while True: # 先进入死循环 因为不知道文件有多大,不知道什么时候停止
while newline in buf: # 刚开始的buf是空的,所以不会进入此循环
pos = buf.index(newline) # 进入循环之后 找到下标 返回下标值
yield buf[:pos] # 返回存储起来的字符串,去除掉隔离符号的部分
buf = buf[pos + len(newline):] # 修改下标值,将分隔符占用的下标加进去
chunk = f.read(4096*10) # 先执行这句,读取文件,字节数4096*10,不能太大
if not chunk: # 读到文件末尾 会进入执行这句
yield buf # 返回buf
break # 跳出死循环
buf += chunk # 拼接字符
with open('demo.txt') as f:
for line in readlines(f, "{|}"):
print(line)