python高级编程第四讲:元类编程

1.property动态属性

2种实现方式

  • 1 通过给一个变量进行方法的实现 例:S=property() 再设置 setter和getter方法
  • 2 直接使用装饰器的方法

2._getattr_和_getattribute_

_getattr_ 在查找不到属性的时候调用 _getattribute_是在_getattr_之前执行的魔法方法,尽量不要重写这个方法

先看一下_getattr_的例子:

from datetime import date,datetime
class User:
    def __init__(self,name,birthday):
        self.name= name
        self.birthday = birthday       
    def __getattr__(self, item):
        print("not find attr")      
    
if __name__ == '__main__':
    user1 = User('zjk',date(year=1989,month = 10,day=1))
    print(user1.age)

执行结果: not find attr None 为什么会有一个none的出现,是因为在打印类的时候 ,程序没有给出返回值,所以就是None

再看一下加了_getattribute_的例子:

from datetime import date,datetime
class User:
    def __init__(self,name,birthday):
        self.name= name
        self.birthday = birthday     
    def __getattr__(self, item):
        print("not find attr")    
   def __getattribute__(self, item):
        return '123'
    
if __name__ == '__main__':
    user1 = User('zjk',date(year=1989,month = 10,day=1))
    print(user1.age)

执行结果: 123 此时我们可以看到结果,程序原本应该是输出 not find attr 的,但是由于 _getattribute_ 是在 _getattr_ 之前执行,所以就直接输出了,而不会再调用 _getattr_ 了,所以在程序中尽量不要重写 _getattribute_ 方法

2.1. _getattr_ 中的item

我们来看一下程序代码:

from datetime import date,datetime

class User:
    def __init__(self,name,birthday):
        self.name= name
        self.birthday = birthday
        
    def __getattr__(self, item):
        print(item)
        print("not find attr")
        

if __name__ == '__main__':
    user1 = User('zjk',date(year=1989,month = 10,day=1))
    print(user1.age)

执行结果: age not find attr None 此结果我们看出来,item其实就是我们在程序调用时给的属性

2.2. 通过_getattr_ 获取类中 字典中的属性

我们来看一下实现代码:

from datetime import date,datetime
class User:
    def __init__(self,name,birthday,info ):
        # info 属性是一个字典形式的
        self.name= name
        self.birthday = birthday
        self.info = info
        
    def __getattr__(self, item):
        return self.info[item]  # 此时是把item 当作字典中的key
     
if __name__ == '__main__':
    user1 = User('zjk',date(year=1989,month = 10,day=1),{"age":18})
    print(user1.age)

执行结果: 18 从程序和结果我们可以看到,通过字典形式取值时,item其实就是我们字典当中的key,也就是把我们调用时传的属性值当作key

我们通过上面的方法,可以自定义一些信息,如果我们写_getattr_方法,当程序中找不到我们要调用的属性时程序会直接报错

3.数据描述符

3.1数据描述符

在类中实现了getsetdelete中任何一个方法,就被称为属性描述符

class IntField(object):
    
    def __get__(self, instance, owner):
        
        print("__get__")
        return self.values  # 这里通过get方法进行值的返回
    
    def __set__(self, instance, value):
        print("__set__")
        
        if not isinstance(value,int):
            raise TypeError
        
        self.values = value  # 这里的value 其实就是设置的属性的值
        #如果这里搞不太明白,我们可以分别 打印输出 instance  和  Value 的值来查看结果
    
    def __delete__(self, instance):
        pass

class User:
    age = IntField
    
    
user = User()

user.age= 20

print(user.age)

3.2 非数据描述符

在类中只实现了 _get_方法

4.自定义元类

元类就是创建类的类,type

4.1 笨方法动态创建类

def new_class(name):
    if name == "user":
        class User:
            def __str__(self):
                return "user"
        return User
    elif name == "person":
        class Person:
            def __str__(self):
                return "Person"
        return Person
    
if __name__ == '__main__':
    
    user1 = new_class('user')
    print(user1)

执行结果: <class 'main.new_class.<locals>.User'>

4.2 通过type方法动态创建类

一般我们不用type方法创建类

我们通过分析type的源,可以知道type还可以动态的创建类,type(类名,由父类组成的元组,包含属性的字典) 程序中类重名,程序不会报错

  • 1 创建没有任何属性的类
# 创建没有任何属性的类

User = type("User",(),{})

obj = User()

print(obj)
  • 2 创建带有属性的类
User = type("User",(),{'name':'zjk','age':18})

obj = User()

print(obj.name)

结果: zjk 如果我们不想在类创建的时候给属性赋值或是空,可以写成 {'name':''} 或是 {'name':None}

  • 3 创建带有属性和方法的类
def info(self):
    return self.name

User = type("User",(),{'name':'zjk','age':18,'info':info})

obj = User()

print(obj.info())

结果:zjk 注意:我们通过 type动态创建类时要添加方法的话也是在属性字典中完成,只不过我们要把方法单 独写出来,并且方法中传的参数是 self ,然后在属性字典中通过 {'info':info}的就去指向 我们自己创建的方法,实例调用 的时候 和我们正常的方法调用形式是一样的 注:私有方法不能通过动态创建类的时候进行引用

  • 4 创建带有继承的类
class BaseClass:
    def demo(self):
        return  "基类"

def info(self):
    return self.name

User = type("User",(BaseClass,),{'name':'zjk','age':18,'info':info})

obj = User()

print(obj.demo())

结果: 基类 注意:我们在元组进行类的继承的时候,写完父类后,一定要在后面加一个 ,号,否则程序会报错,这一点要特别注意

5.metaclass属性

如果一个类中定义了_metalass_ = xxx,Python就会用元类的方式来创建类 也就是我们可以通过 metaclass 这种方式自定义类的一些实现方式

  • python2 与 python 使用的区别

python2 中示例:

class A:
    \__metalass\__ = xxx

python 3 中示例:

class B(object,metaclass = xxx):
  • 使用示例:
def upper_attr(class_name,class_parents,class_attr): # 相当于type创建类时传递的参数,名字可以是任意的,因为这里是形参
    newattr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            newattr[name.upper()]=value
            
            
    return type(class_name,class_parents,newattr)
    


class User(object,metaclass=upper_attr):
    name = 'zjk'

print(hasattr(User,'name'))
print(hasattr(User,'NAME'))

执行结果: False True 通过结果我们可以看出,我们在创建类的时候,如果指定了 metaclass 程序会先按照我们指定的方法或类进行创建

6.迭代器和生成器

6.1迭代器

在介绍迭代器之前,先说明下迭代的概念: 迭代:通过for循环遍历对象的每一个元素的过程。 Python的for语法功能非常强大,可以遍历任何可迭代的对象。 在Python中,list/tuple/string/dict/set/bytes都是可以迭代的数据类型。

6.1.1迭代器是什么?

迭代器是一种可以被遍历的对象,并且能作用于next()函数。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。 迭代器只能往后遍历不能回溯,不像列表,你随时可以取后面的数据,也可以返回头取前面的数据。

6.1.2判断列表是否是迭代器,是否是可以迭代的

from collections import Iterable,Iterator

print(isinstance(list(),Iterable))
print(isinstance(list(),Iterator))

6.1.3列表转换成迭代器

lis = [2,3,4]
it =iter(lis)

print(isinstance(it,Iterator))

6.1.4 next 和 for 遍历 迭代器的区别

next当迭代不出数据时,程序会报错,for循环当迭代不出数据时候,会自动停止

6.2生成器

有时候,序列或集合内的元素的个数非常巨大,如果全制造出来并放入内存,对计算机的压力是非常大的。

所以此时我们需要用 yield关键字 我们看一段代码:

def createNum():
    a,b = 0,1
    for i in range(5):
        a,b = b,a+b
        yield b
        

g = createNum()
print(g)

for i in g:
    print(i)

执行结果: 1 2 3 5 8 其实看结果我们可以看到上面的程序其实完成的是一个输出 斐波纳契 数列

6.2.1 return 和 yield 的区别

return会直接返回一次的值,但是 yield 会在yield时候程序会进行暂停,并且保存数据,并且会继续循环,直至循环完成

7 生成器读取大文件

场景:300G的一个特殊大文件,只有一行,每段数据由 ‘|’ 分隔 我们需要编写一个程序来读取相应内容

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(1024*10)
        if not chunk:
            yield buf
            break
        buf += chunk

with open('1.txt') as f:
    for line in readlines(f,'|'):
        print(line)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券