python3基础知识复习 -- 魔法方法之描述符,定制序列,迭代器

描述符descriptor(property原理)

  • 定义:将某种特殊类的实例指派给另一个类的属性

  • 特殊类:至少实现以下三个方法中的一个(描述符属性方法)

__get__(self, instance, owner) – 用于访问属性和返回它的值

__set__(self, instance, value) – 将在属性分配时调用,不返回任何值

__delete__(self, instance) – 控制删除操作,不返回任何值

  1. 重写property

    """ write a property class """
    class Myproperty:
        def __init__(self, fget=None, fset=None, fdel=None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
     
        def __get__(self, instance, owner):
            print("geting...", self, ",", instance, ",", owner)
            return self.fget(instance)
     
        def __set__(self, instance, value):
            print("setting...", self, ",", instance, ",", value)
            self.fset(instance, value)
     
        def __delete__(self, instance):
            print("deleting...", self, ",", instance)
            self.fdel(instance)
            
     
    class C:
        def __init__(self, _x=1):
            self._x = _x
     
        def getX(self):
            return self._x
     
        def setX(self, value):
            self._x = value
     
        def delX(self):
            del self._x
     
        x = Myproperty(getX, setX, delX)
    
    

2) 摄氏华氏度转换,celsius x 1.8 + 32 = fahrenheit

在这里插入图片描述

	''' celsius 2 fahrenheit via descriptor'''
	 
	class Celsius:
	    def __init__(self, temp=26.0):
	        self.temp = float(temp)
	 
	    def __get__(self, instance, owner):
	        return self.temp
	    
	    def __set__(self, instance, value):
	        self.temp = float(value)
	 
	class Fahrenheit:
	    def __get__(self, instance, owner):
	        return instance.cel * 1.8 + 32
	 
	    def __set__(self, instance, value):
	        instance.cel = (float(value) -32) / 1.8
	    
	 
	class Temperature:
	    cel = Celsius()
	    fah = Fahrenheit()
    
    t = Temp()
    # t.cel = 100
    t.fah = 100
    print(t.cel)
    print(t.fah)	
	
   # 计算属性赋值的次数 
    class Counter:
	    k = []
	    def __init__(self):
	        self.counter = 0
	        
	    def __setattr__(self, name, value):
	        if name != "counter":
	            if name not in self.k:
	                self.counter += 1
	                self.k.append(name)
	                
	        super().__setattr__(name, value)
	        
	
	    def __delattr__(self, name):
	        self.counter -= 1
	        self.k.remove(name)
	        super().__delattr__(name)

魔法方法之描述符

  • 什么是描述符——descriptor以及相关的一系列定义

(1)描述符:某个类,只要是内部定义了方法 get, set, delete 中的一个或多个,就可以称为描述符,描述符的本质是一个类。

(2)描述符协议:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这些魔法方法也被称为描述符协议.

(3)非数据描述符:一个类,如果只定义了 __get__() 或者是__delete__()方法,而没有定义 __set__()方法,则认为是非数据描述符

(4)数据描述符:一个类,不仅定义了 __get__() 方法,还定义 __set__(), __delete__() 方法,则认为是数据描述符

(5)描述符对象:描述符(即一个类,因为描述符的本质是类)的一个对象,一般是作为其他类对象的属性而存在.

  • *绑定行为:**所谓的绑定行为,是指在属性的访问、赋值、删除时还绑定发生了其他的事情。

托管属性:python描述符是一种创建“托管属性”的方法,即通过描述符(类)去托管另一个类的相关属性,也可以说是类的属性的一个代理。其实就是专门用一个类去装饰某一个属性,我可以把这个属性定义成任何我想要的样子,所谓的*“一对一定制属性”**。一个类属性本质上就是属性描述类的一个实例对象.

描述符三个函数的定义形式:

def __get__(self, instance, owner)

self:指的是描述符类的实例

instance:指的是使用描述符的那个类的实例

owner:指的是使用描述符的那个类

def __set__(self, instance, value)

def __delete__(self, instance)

总结:对于类属性描述符**,如果解析器发现属性property是一个描述符的话,它能把Class.x转换成Class.dict[‘property’].get(None, Class)来访问。

类属性描述符

总结:

(1)对于类装饰器属性,只要出现属性访问(不管是通过对象访问还是类名访问),都会优先调用装饰器的__get__方法;

(2)对于类装饰器属性,若出现属性修改(不管是通过对象访问还是类名访问),都会优先调用装饰器的__set__方法;

(3)对于类装饰器属性,若出现属性删除(不管是通过对象访问还是类名访问),都会优先调用装饰器的__delete__方法;

实例属性描述符

*总结:*描述符是一个类属性,必须定义在类的层次上, 而不能单纯的定义为对象属性。

Eg:
>>> class MyDes:
        def __init__(self, value = None):
                self.val = value
        def __get__(self, instance, owner):
                return self.val ** 2

>>> class Test:
        def __init__(self):
                self.x = MyDes(3)

>>> test = Test()
>>> test.x
<__main__.MyDes object at 0x1058e6f60>
##访问实例层次上的描述符 x,只会返回描述符本身。为了让描述符能够正常工作,它们必须定义在类的层次上。如果你不这么做,那么 Python 无法自动为你调用 __get__ 和 __set__ 方法

''' use pickle to store the attribute of a var, used with descriptor '''
import os
import pickle

class Mydes:
    saved = []
    
    def __init__(self, var=None):
        self.var = var
        self.f_name = self.var + '.pkl'

    def __get__(self, instance, owner):
        if self.var not in Mydes.saved:
            raise AttributeError('No value set for %s, wahaha' % self.var)

        with open(self.f_name, 'rb') as f:
            value = pickle.load(f)
            
        return value

    def __set__(self, instance, value): 
        with open(self.f_name, 'wb') as f:
            pickle.dump(value, f)

        Mydes.saved.append(self.var)
        
            
    def __delete__(self, instance):
        os.remove('%s.pkl' %self.var)
        Mydes.saved.remove(self.var)
        

class Test:
    x = Mydes('x')
    y = Mydes('y')

test = Test()

魔法方法之定制序列定制容器

Protocols 与其他编程语言中的接口很相似,规定了那些方法必须要定义。然鹅在Python中协议显得不那么重视,更像是一种指南/建议。

容器类型的协议:

容器类型
__len__(self)定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key)定义获取容器中指定元素的行为,相当于 self[key]
__setitem__(self, key, value)定义设置容器中指定元素的行为,相当于 self[key] = value
__delitem__(self, key)定义删除容器中指定元素的行为,相当于 del self[key]
__iter__(self)定义当迭代容器中的元素的行为
__reversed__(self)定义当被 reversed() 调用时的行为
__contains__(self, item)定义当使用成员测试运算符(in 或 not in)时的行为
  1. 希望定制的容器是不可变的,需要定义__len__()__getitem__()方法, eg,元组,字符串
  2. 如果希望定制容器可变的话,除了__len__()__getitem__()外还需定义__setitem__()__delitem__()方法,eg. 列表list, 字典dict的赋值、删除等
Eg. 定制不可变列表并可以记录每个元素被访问的次数

class Mylist:	
	def __init__(self, *args):
		self.values = [x for x in args]
		self.count = {}.fromkeys(range(len(self.values)), 0) # 每个参数都做成了一个dict,value都是0

	def __len__(self):
		return len(self.values)

	def __getitem__(self, key):
		self.count[key] += 1
		return self.values[key]

Eg:

定制一个列表,同样要求记录列表中每个元素被访问的次数。这一次我们希望定制的列表功能更加全面一些,比如支持 append()、pop()、extend() 原生列表所拥有的方法。你应该如何修改呢?

要求1:实现获取、设置和删除一个元素的行为(删除一个元素的时候对应的计数器也会被删除)

要求2:增加 counter(index) 方法,返回 index 参数所指定的元素记录的访问次数

要求3:实现 append()、pop()、remove()、insert()、clear() 和 reverse() 方法(重写这些方法的时候注意考虑计数器的对应改变)

class CountList(list):
    def __init__(self, *args):
        super().__init__(args)
        self.count = []
        for i in args:
            self.count.append(0) # 初始化参数的计数器

    def __len__(self):
        return len(self.count)

    def __getitem__(self, key):
        self.count[key] += 1
        return super().__getitem__(key)

    def __setitem__(self, key, value):
        self.count[key] += 1
        super().__setitem__(key, value)

    def __delitem__(self, key):
        del self.count[key]
        super().__delitem__(key)

    def counter(self, key):
        return self.count[key]

    def append(self, value):
        self.count.append(0)
        super().append(value)

    def pop(self, key=-1):
        del self.count[key]
        return super().pop(key)

    def remove(self, value):
        key = super().index(value)
        del self.count[key]
        super().remove(value)

    def insert(self, key, value):
        self.count.insert(key, 0)
        super().insert(key, value)

    def clear(self):
        self.count.clear()
        super().clear()

    def reverse(self):
        self.count.reverse()
        super().reverse()

魔法方法之迭代器

内置BIF:

  • Iter() — 制作迭代器
    iter() – return itself
  • Next() – 读取下一个元素,没有元素可以列举时报 StopIteration 异常
    next() — 重点,决定了如何得到下一个元素

实现了__iter__ __next__魔法方法的类就可以作为迭代器

Eg1: 使用while实现for 循环

a = iter(range(10))   #range是一种range object,仅实现了__iter__但是没有__next__,不能作为迭代器,iter将他变成了迭代器

while True:
    try:
    	each = next(a)

    except StopIteration:
        break
        print(each)

Eg2: 斐波那契数列

class Fibs:
	def __init__(self, n=10):
        self.a,self.b = 0, 1
        self.n = n

    def __iter__(self):
       	return self

    def __next__(self):
		self.a, self.b = self.b, self.a + self.b
        return self.a

fibs = Fibs(100)

for each in fibs:
    if each < fibs.n:
        print(each)
    else:
        Break
 
Eg3: 打印出闰年(迭代器)
import datetime as dt
 
class LeapYear:
    def __init__(self):
        self.now = dt.date.today().year
 
    def isLeapYear(self, year):
        if (year%4 == 0 and year%100 != 0) or (year%400 == 0):
            return True
        else:
            return False
        
    def __iter__(self):
        return self
 
    def __next__(self):
        while not self.isLeapYear(self.now):
            self.now -= 1 
 
        temp = self.now
        self.now -= 1
        
        return temp
 
Eg4: 迭代器实现reversed
class MyRev:
    def __init__(self, data):
        self.data = data
        self.index = len(data)
        
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index == 0:
            raise StopIteration
 
        self.index = self.index - 1
        return self.data[self.index]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值