python包装与授权

简介: python包装与授权

所有便准数据类型都可以通过两种方式产生,一种是直接定义,一种是使用相应的函数,比如要产生字符串,可以s=’hello’或s=str(hello)。像str这种函数都是工厂函数,实际上工厂函数都是类,我们可以通过继承这些类来定制自己的数据类型。(包装)

包装

python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)。

二次加工标准类型(基于继承实现)

class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid
    def append(self, p_object):
        ' 派生自己的append:加上类型检查'
        if not isinstance(p_object,int): #if type(p_object) is int:
            raise TypeError('must be int')
        #self.append(p_object) #死循环
        #list.append(self, p_object) #调用父类的方法
super().append(p_object) #调用父类的方法
    @property
    def mid(self):
        '新增自己的属性'
        index=len(self)//2
        return self[index]
l=List([1,2,3,4])
print(l)
l.append(5)
print(l)
# l.append('1111111') #报错,必须为int类型
print(l.mid)
#其余的方法都继承list的
l.insert(0,-123)
print(l)
l.clear()
print(l)

clear加权限限制

class List(list):
    def __init__(self,item,tag=False):
        super().__init__(item)
        self.tag=tag
    def append(self, p_object):
        if not isinstance(p_object,str):
            raise TypeError
        super().append(p_object)
    def clear(self):
        if not self.tag:
            raise PermissionError
        super().clear()
l=List([1,2,3],False)
print(l)
print(l.tag)
l.append('saf')
print(l)
# l.clear() #异常
l.tag=True
l.clear()

授权

授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

授权是采用已存在的功能达到最大限度的代码重用。在包装中我们可以新建、修改或删除已有的功能,授权的过程就是将更新的功能交由新类来处理,已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法 (包装是通过继承实现)。在代码里包含一个对getattr()内建函数的调用,调用getattr()得到默认对象的属性(数据属性或者方法)并返回它,以便于访问或者调用。

当引用一个属性时,解释器首先会在局部名称空间中查找那个名字,比如一个自定义的方法或局部实例属性。如果没有在局部字典中找到,则搜索类名称空间,以防一个类属性被访问。最后,如果两类搜索都失败了,搜索则对原对象开始授权请求,此时__getattr__()会被调用。

示例1

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line): #定制自己的write方法 – 对写入的内容加上时间
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))
    def __getattr__(self, item):
        return getattr(self.file,item)# self.file是通过系统默认的open获取的,它包含了默认open的所有方法,item是一个字符串,getattr以字符串形式调用方法
f1=FileHandle('b.txt','w+')
f1.write('你好啊')
f1.seek(0)
print(f1.read()) 
f1.close()

在实例和类FileHandle中都没有找到该属性,所以触发__getattr__先找f1的属性字典,没有则找类FileHandle的属性字典,没有则触发__getattr__,__getattr__中调用的是系统open返回的文件描述符的方法,通过自己的类实例化,调用系统的方法。

示例2

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#我们来加上b模式支持
import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        if 'b' in mode:
            self.file=open(filename,mode)
        else:
            self.file=open(filename,mode,encoding=encoding)
        self.filename=filename
        self.mode=mode
        self.encoding=encoding
    def write(self,line):
        if 'b' in self.mode:
            if not isinstance(line,bytes):
                raise TypeError('must be bytes')
        self.file.write(line)
    def __getattr__(self, item):
        return getattr(self.file,item)
    def __str__(self):
        if 'b' in self.mode:
            res="<_io.BufferedReader name='%s'>" %self.filename
        else:
            res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding)
        return res
f1=FileHandle('b.txt','wb')
# f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了
f1.write('你好啊'.encode('utf-8'))
print(f1)
f1.close()

示例3

class List:
    def __init__(self,seq):
        self.seq=seq
    def append(self, p_object):
        ' 派生自己的append加上类型检查,覆盖原有的append'
        if not isinstance(p_object,int):
            raise TypeError('must be int')
        self.seq.append(p_object)
    @property
    def mid(self):
        '新增自己的方法'
        index=len(self.seq)//2
        return self.seq[index]
    def __getattr__(self, item):
        return getattr(self.seq,item)
    def __str__(self):
        return str(self.seq)
l=List([1,2,3])
print(l)
l.append(4)
print(l)
# l.append('3333333') #报错,必须为int类型
print(l.mid)
#基于授权,获得insert方法
l.insert(0,-123)
print(l)

示例4

class List:
    def __init__(self,seq,permission=False):
        self.seq=seq
        self.permission=permission
    def clear(self):
        if not self.permission:
            raise PermissionError('not allow the operation')
        self.seq.clear()
    def __getattr__(self, item):
        return getattr(self.seq,item)
    def __str__(self):
        return str(self.seq)
l=List([1,2,3])
# l.clear() #此时没有权限,抛出异常
l.permission=True
print(l)
l.clear()
print(l)
#基于授权,获得insert方法
l.insert(0,-123)
print(l)

__setattr__,__delattr__,__getattr__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y
    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')
    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,因为设置属性会触发__setattr__
        # self.__dict__[key]=value #应该使用它,直接修改底层字典
    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)# 直接在底层字典删除,就不会触发了
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx

getattr(obj)相当于obj.__getattr__()

setattr(obj)相当于obj.__setattr__()

delattr(obj)相当于obj.__delattr__()

  • 类的内置attr属性,你不定义的话会用默认的,如果定义了则用自己定义的。
  • class.at 调用时触发:如果类class中属性at不存在,则触发__getattr__
  • del class.at删除时触发:删除类class的属性at时,触发__delattr__
  • class.at=1设置时触发:设置类class的属性at时,触发__setattr__

自己定义这三个函数,在相应操作时,就可以触发自己想要的逻辑。

class MyClass:
def __init__(self, name)
    self.name = name #触发__setattr__
def __getattr__(self, item) #默认的__getattr__是属性不存在则报错,我们修改后不报错,仅打印提示
    print(“attr [%s] not found” %item)
def __setattr__(self, k, v) #默认的setattr会把设置的属性加在属性字典,但是我们自定义的setattr什么也没做,属性字典不会增加属性
    print(‘set attr’, k, v)
    if tyoe(v) is str: #限制value只能以字符串的形式设置
        print(‘set’)
        #self.k = v #再次触发__setattr__,进入死循环
        self.__dict__[k] = v.upper() 
#真正的设置 – 直接操作底层数据结构,在属性字典中添加
    else:
        print(‘not str’)
def __delattr__(self, item)
    """ #自己控制-所有属性不可删除
    print(‘can not delete [%s]’ %item)
    pass
    """
    print(‘delete attr[%s]’ %item)
    #del self.item #触发__delattr__ 进入死循环
    self.__dict__.pop(item) #真正的删除
mc = MyClass(‘hello’) #实例化的时候会触发__init__
print(mc.name) #hello
print(mc.hhh) #触发__getattr__  mcself  hhhitem(字符串的形式)
mc.age = 18
mc.age = ‘18’ #触发__setattr__  mcself  agek  ‘18’v
del mc.name #触发__delattr__  mcself  name item(字符串的形式)

__getattribute__

#__getattr__
class Foo:
    def __init__(self,x):
        self.x=x
    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
#__getattribute__ - 不管属性是否存在,都会触发
class Foo:
    def __init__(self,x):
        self.x=x
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
f1=Foo(10)
f1.x
f1.xxxxxx

如果二者同时出现

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
class Foo:
    def __init__(self,x):
        self.x=x
    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]
    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈') #抛出AttributeError异常–跳转到__getattr__
f1=Foo(10)
f1.x
f1.xxxxxx

当__getattribute__与__getattr__同时存在,只会执行__getattribute__,除非__getattribute__在执行过程中抛出异常AttributeError,系统默认的__getattribute__,如果查找的属性不存在,当抛出异常AttributeError的时候,会跳转到__getattr__,也就是说,只有抛出异常AttributeError的时候,才会执行__getattr__,其他情况下只会执行__getattribute__。系统提供的默认__getattribute__,当要查找的属性存在时,会在当前属性字典中返回属性,如果要找的属性不存在会抛出异常AttributeError,转到__getattr__去执行,__getattr__接收异常来避免程序崩溃,并执行定义好的逻辑。

618图书推荐

书籍是知识的海洋,计算机好书推荐

🔥🔥🔥618,清华社 IT BOOK 多得图书活动开始啦!活动时间为2023 年6 月7 日至6 月18 日,清华社为您精选多款高分好书,涵盖了 C++、Java、Python、前端、后端、数据库、算法与机器学习等多个IT 开发领域,适合不同层次的读者。全场5 折,扫码领券更有优惠哦!快来京东点击链接 IT BOOK多得查看详情吧!


相关文章
|
7月前
|
API 数据安全/隐私保护 Python
Python中使用`requests`库进行身份验证与授权
【4月更文挑战第12天】在Python的网络编程中,许多API和Web服务要求用户进行身份验证和授权,以确保只有经过认证的用户才能访问特定的资源或执行特定的操作。`requests`库作为Python中流行的HTTP客户端库,提供了灵活且强大的身份验证和授权机制。本文将详细介绍如何在Python中使用`requests`库进行身份验证与授权。
|
7月前
|
Python
Python Web 开发: 如何在 Flask 中实现用户认证和授权?
Python Web 开发: 如何在 Flask 中实现用户认证和授权?
386 0
|
数据安全/隐私保护 Python
Python 技术篇-连接qq邮箱服务器,调用qq邮箱发送邮件实战演示,qq邮箱授权码开通方法
Python 技术篇-连接qq邮箱服务器,调用qq邮箱发送邮件实战演示,qq邮箱授权码开通方法
624 0
Python 技术篇-连接qq邮箱服务器,调用qq邮箱发送邮件实战演示,qq邮箱授权码开通方法
|
存储 JSON 安全
STS Python_SDK授权临时用户读写OSS资源
这里将手动定义 授权策略(Policy),将 授权策略 授权给 角色也可授权给RAM子账号,这里不作展示 ,然后子账号(RAM account)通过 扮演角色方法 获取 角色 的 安全令牌即临时身份 对 资源 进行操作. RAM 用户 可以使用 API 扮演 RAM 角色。当 RAM 用户被授予 AliyunSTSAssumeRoleAccess 权限策略 之后,可以使用其访问密钥调用 STS API AssumeRole 接口,以获取某个角色的 安全令牌临时身份,从而使用安全令牌访问资源。
1083 0
|
SQL 关系型数据库 Linux
Python全栈 MySQL 数据库 (SQL查询、备份、恢复、授权)
MySQL的数据安全、授权、备份、恢复
2874 0
|
17天前
|
人工智能 数据可视化 数据挖掘
探索Python编程:从基础到高级
在这篇文章中,我们将一起深入探索Python编程的世界。无论你是初学者还是有经验的程序员,都可以从中获得新的知识和技能。我们将从Python的基础语法开始,然后逐步过渡到更复杂的主题,如面向对象编程、异常处理和模块使用。最后,我们将通过一些实际的代码示例,来展示如何应用这些知识解决实际问题。让我们一起开启Python编程的旅程吧!
|
16天前
|
存储 数据采集 人工智能
Python编程入门:从零基础到实战应用
本文是一篇面向初学者的Python编程教程,旨在帮助读者从零开始学习Python编程语言。文章首先介绍了Python的基本概念和特点,然后通过一个简单的例子展示了如何编写Python代码。接下来,文章详细介绍了Python的数据类型、变量、运算符、控制结构、函数等基本语法知识。最后,文章通过一个实战项目——制作一个简单的计算器程序,帮助读者巩固所学知识并提高编程技能。
|
4天前
|
Unix Linux 程序员
[oeasy]python053_学编程为什么从hello_world_开始
视频介绍了“Hello World”程序的由来及其在编程中的重要性。从贝尔实验室诞生的Unix系统和C语言说起,讲述了“Hello World”作为经典示例的起源和流传过程。文章还探讨了C语言对其他编程语言的影响,以及它在系统编程中的地位。最后总结了“Hello World”、print、小括号和双引号等编程概念的来源。
98 80
|
23天前
|
存储 索引 Python
Python编程数据结构的深入理解
深入理解 Python 中的数据结构是提高编程能力的重要途径。通过合理选择和使用数据结构,可以提高程序的效率和质量
134 59
下一篇
DataWorks