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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354