前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python高级编程第四讲:元类编程

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

作者头像
小海怪的互联网
发布2019-08-23 16:41:21
4380
发布2019-08-23 16:41:21
举报

1.property动态属性

2种实现方式

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

2._getattr_和_getattribute_

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

先看一下_getattr_的例子:

代码语言:javascript
复制
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_的例子:

代码语言:javascript
复制
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

我们来看一下程序代码:

代码语言:javascript
复制
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_ 获取类中 字典中的属性

我们来看一下实现代码:

代码语言:javascript
复制
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中任何一个方法,就被称为属性描述符

代码语言:javascript
复制
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 笨方法动态创建类

代码语言:javascript
复制
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 创建没有任何属性的类
代码语言:javascript
复制
# 创建没有任何属性的类

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

obj = User()

print(obj)
  • 2 创建带有属性的类
代码语言:javascript
复制
User = type("User",(),{'name':'zjk','age':18})

obj = User()

print(obj.name)

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

  • 3 创建带有属性和方法的类
代码语言:javascript
复制
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 创建带有继承的类
代码语言:javascript
复制
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 中示例:

代码语言:javascript
复制
class A:
    \__metalass\__ = xxx

python 3 中示例:

代码语言:javascript
复制
class B(object,metaclass = xxx):
  • 使用示例:
代码语言:javascript
复制
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判断列表是否是迭代器,是否是可以迭代的

代码语言:javascript
复制
from collections import Iterable,Iterator

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

6.1.3列表转换成迭代器

代码语言:javascript
复制
lis = [2,3,4]
it =iter(lis)

print(isinstance(it,Iterator))

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

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

6.2生成器

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

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

代码语言:javascript
复制
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的一个特殊大文件,只有一行,每段数据由 ‘|’ 分隔 我们需要编写一个程序来读取相应内容

代码语言:javascript
复制
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)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.property动态属性
  • 2._getattr_和_getattribute_
    • 2.1. _getattr_ 中的item
      • 2.2. 通过_getattr_ 获取类中 字典中的属性
      • 3.数据描述符
        • 3.1数据描述符
          • 3.2 非数据描述符
          • 4.自定义元类
            • 4.1 笨方法动态创建类
              • 4.2 通过type方法动态创建类
              • 5.metaclass属性
              • 6.迭代器和生成器
                • 6.1迭代器
                  • 6.1.1迭代器是什么?
                  • 6.1.2判断列表是否是迭代器,是否是可以迭代的
                  • 6.1.3列表转换成迭代器
                  • 6.1.4 next 和 for 遍历 迭代器的区别
                • 6.2生成器
                  • 6.2.1 return 和 yield 的区别
              • 7 生成器读取大文件
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档