python get 函数-python的__get__、__set__、__delete__(1)

内容:

描述符引导

摘要

定义和介绍

描述符协议

调用描述符

样例

Properties

函数和方法

静态方法和类方法

摘要

定义并展示如何调用描述符,展示自定义描述符和几个内置的python描述符,包括函数、属性、静态方法和类方法,通过给出一个Python的示例应用来展示描述符是如何工作的.

熟练掌握描述符不仅让你拥有python使用的额外技巧,并且可以加深对Python内部如何工作的理解,提升对程序设计的能力,而且体会到python的设计优雅之处

定义和介绍

一般来说,描述符是带有“绑定行为”的对象属性,它的属性访问已经被描述符协议中的方法覆盖了.这些方法是__get__(),__set__(),和__delete__().

如果一个对象定义了这些方法中的任何一个,它就是一个描述符.

默认的属相访问是从对象的字典中 get, set, 或者 delete 属性,;例如a.x的查找顺序是:

a.x -> a.__dict__["x"] -> type(a).__dict__["x"] -> type(a)的基类(不包括元类),如果查找的值是对象定义的描述方法之一,python可能会调用描述符方法来重载默认行为,

发生在这个查找环节的哪里取决于定义了哪些描述符方法

注意,只有在新式类中描述符才会起作用(新式类继承type或者object class)

描述符是强有力的通用协议,属性、方法、静态方法、类方法和super()背后使用的就是这个机制,描述符简化了底层的c代码,并为Python编程提供了一组灵活的新工具

描述符协议

descr.__get__(self, obj, type=None) ->value

descr.__set__(self, obj, value) ->None

descr.__delete__(self, obj) -> None

定义任何上面三个方法的任意一个,这个对象就会被认为是一个描述符,并且可以在被作为对象属性时重载默认的行为, 如果一个对象定义了__get__() 和 __set__(),它被认为是一个数据描述符.只定义 __get__()被认为是非数据描述符,数据和非数据描述符的区别在于:如果一个实例的字典有和数据描述符同名的属性,那么数据描述符会被优先使用,如果一个实例的字典实现了无数据描述符的定义,那么这个字典中的属性会被优先使用,实现只读数据描述符,同时定义__get__()和__set__(),在__set__()中抛出AttributeError.

描述符调用

描述符可以直接用方法名称调用,比如:d.__get__(obj)

然而,描述符更常用的方式是属性访问时被自动调用,例如:obj.d 在obj的字典中查找d,如果d定义了方法__get__(),然后d.__get__(obj)会被通过下面的优先级列表调用

详细的调用依赖于obj是一个对象还是一个类,不管哪种方式,描述符只工作在新式对象和类,如果一个类是object的子类(继承object),这个类就是一个新式类

对于对象来说,object.__getattribute__() 把b.x 变为 type(b).__dict__["x"].__get__(b, type(b)) .优先级顺序:

数据描述符 > 实例变量 > 非数据描述符,__getattr__()具有最低优先级(如果实现了的话),C语言的实现可以在 Objects/object.c 中 PyObject_GenericGetAttr() 查看.

对于类来说,type.__getattribute__() 把 B.x 变为 B.__dict__["x"].__get__(None, B),代码实现为:

def __getattribute__(self, key):"Emulate type_getattro() in Objects/typeobject.c"v= object.__getattribute__(self, key)if hasattr(v, "__get__"):return v.__get__(None, self)return v

重点:

描述符被__getattribute()方法调用

重载__getattribute__()会阻止描述符自动调用

__getattribute__()只适用于新式类和对象

object.__getattribute__()和type.__getattribute__()对__get__()的调用不一样

数据描述符会重载实例字典

非数据描述符可能会被实例字典重载

super()返回的对象会使用定制__getattribute__()方法来调用描述符,调用super(B, obj).m() 会在紧邻着B的基类A搜索obj.__class__.__mro__然后返回A.__dict__["m"].__get__(obj, B),如果不是一个描述符,返回未改变的m

如果不在字典中,m会调用 object.__getattribute__() 查询

注意:在python2.2,如果m是一个数据描述符,super(B, obj).m() 会调用__get__(),在python2.3,无数据描述符也会执行调用,除非是个旧式类,super_getattro() 的细节在Objects/typeobject.c中

上面展示的是描述符在object, type, and super() 的 __getattribute__() 方法中的实现机制,继承object的类自动实现或者他们有一个元类提供类似的功能,同样,重载 __getattribute__()可以停止描述符的调用

描述符例子

下面的代码创建了一个类,每次访问get或者set都会打印一条信息.重载__getattribute__()也可以使每个属性实现这一方法,然而,描述符在查看特定的属性时比较有用

classRevealAccess(object):"""A data descriptor that sets and returns values

normally and prints a message logging their access."""

def __init__(self, initval=None, name="var"):

self.val=initval

self.name=namedef __get__(self, obj, objtype):print "Retrieving", self.namereturnself.valdef __set__(self, obj, val):print "Updating", self.name

self.val=val>>> classMyClass(object):

... x= RevealAccess(10, "var "x"")

... y= 5...>>> m =MyClass()>>>m.x

Retrieving var"x"

10

>>> m.x = 20Updating var"x"

>>>m.x

Retrieving var"x"

20

>>>m.y5

这个协议很简单却又可以提供令人为之一振的可能性.Properties, bound 和 unbound methods, 静态方法和 类方法 都是基于描述符协议

Properties

调用property()是一种建立数据描述符的方便方法,可以在访问一个属性的时候触发方法的调用

property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

下面展示一个定义管理属性x的典型的样例:

classC(object):def getx(self):return self.__x

def setx(self, value):self.__x =valuedef delx(self):del self.__xx= property(getx, setx, delx, "I"m the "x" property.")

property()使用纯python方式实现描述符:

classProperty(object):"Emulate PyProperty_Type() in Objects/descrobject.c"

def __init__(self, fget=None, fset=None, fdel=None, doc=None):

self.fget=fget

self.fset=fset

self.fdel=fdelif doc is None and fget is notNone:

doc= fget.__doc__self.__doc__ =docdef __get__(self, obj, objtype=None):if obj isNone:returnselfif self.fget isNone:raise AttributeError("unreadable attribute")returnself.fget(obj)def __set__(self, obj, value):if self.fset isNone:raise AttributeError("can"t set attribute")

self.fset(obj, value)def __delete__(self, obj):if self.fdel isNone:raise AttributeError("can"t delete attribute")

self.fdel(obj)defgetter(self, fget):return type(self)(fget, self.fset, self.fdel, self.__doc__)defsetter(self, fset):return type(self)(self.fget, fset, self.fdel, self.__doc__)defdeleter(self, fdel):return type(self)(self.fget, self.fset, fdel, self.__doc__)

当用户接口已经授权访问属性,这时候需求发生变化,property()可以提供便利, 例如,一个电子表格类可以通过单元("b10")授予对单元格值的访问权.这时候,对程序的后续改进要求在每次访问时重新计算单元格的值;然而,程序员不希望影响现有客户端代码.解决方案是在属性数据描述符中封装对value属性的访问:

classCell(object):

. . .defgetvalue(self):"Recalculate the cell before returning value"self.recalc()returnself._value

value= property(getvalue)

函数和方法

python的面向对象是建立在函数的基础上,使用非数据描述符,两者会结合的非常紧密.

类的字典将方法比作函数存储.在一个类的定义中,使用def和lambda来声明方法,这是用于创建函数的常用工具. 唯一不同之处,就是第一个参数用来表示对象实例,python约定,实例引用可以使self或者this或者其他变量名称

为了支持方法调用,函数通过__get__()方法来实现属性访问时的方法绑定

这说明所有的函数都是非数据描述符,它返回绑定或者非绑定方法依赖于它被对象还是类调用

在python中的实现如下:

classFunction(object):

. . .def __get__(self, obj, objtype=None):"Simulate func_descr_get() in Objects/funcobject.c"

return types.MethodType(self, obj, objtype)

在解释器中展示函数描述符如何运行:

>>> classD(object):

...deff(self, x):

...returnx

...>>> d =D()>>> D.__dict__["f"] #Stored internally as a function

>>> D.f #Get from a class becomes an unbound method

>>> d.f #Get from an instance becomes a bound method

>

输出说明绑定和未绑定方法是两种不同类型,PyMethod_Type在 Objects/classobject.c 中实际的C实现是一个具有有两种不同表现形式的单一对象,依赖于im_self是set还是null(等价C中的None)

同样,调用方法对象的效果依赖于im_self,如果set(绑定),原函数(存储在im_func中)被调用,它的第一个参数设置为实例.

如果unbound,所有的参数不做改变的传给原函数,instancemethod_call()的C实现因为包含一些类型检查会复杂一些

静态方法和类方法

无数据描述符提供一种简单的机制将函数绑定为方法

简单地说,函数的__get__()方法会将函数被作为属性访问时转换为方法,非数据描述符将 obj.f(*args) 调用为f(obj, *args).调用 klass.f(*args)变为f(*args)

下面的表格汇总了绑定和它常见的两种变化

Transformation Called from an Object Called from a Class

function   f(obj, *args)    f(*args)

staticmethod   f(*args)     f(*args)

classmethod f(type(obj), *args) f(klass, *args)

调用c.f 或者 C.f等价于 object.__getattribute__(c, "f") 或者 object.__getattribute__(C, "f"),不管从对象还是类中,这个函数都可以访问到

不需要self变量的方法适合使用静态方法

例如:一个统计包可能包括用于实验数据的容器类,该类提供了用于计算依赖于数据的平均、平均值、中值和其他描述性统计数据的常规方法.可是可能有一些概念相关但不依赖数据的函数.

例如:erf(x)是一个不依赖于特定数据集的函数,它可以从一个类或者函数调用:s.erf(1.5) --> .9332 或者 Sample.erf(1.5) --> .9332

静态方法返回原始函数:

>>> classE(object):

...deff(x):

...printx

... f=staticmethod(f)

...>>> print E.f(3)3

>>> print E().f(3)3

python版本使用非数据描述符的实现方法:

classStaticMethod(object):"Emulate PyStaticMethod_Type() in Objects/funcobject.c"

def __init__(self, f):

self.f=fdef __get__(self, obj, objtype=None):return self.f

与静态方法不同,类方法在调用函数之前先将类的引用预添加到参数列表中.调用者不管是对象还是类,这种格式是相同的

>>> classE(object):

...deff(x):

...printx

... f=staticmethod(f)

...>>> print E.f(3)3

>>> print E().f(3)3

这种行为在函数只需要有类引用且不关心任何底层数据的情况下是有用的,类方法的一个用途是用来创建不同的类构造器,在python2.3中,类方法dict.fromkeys()可以使用一个key的列表来创建字典,python的实现方式:

classDict(object):

. . .def fromkeys(klass, iterable, value=None):"Emulate dict_fromkeys() in Objects/dictobject.c"d=klass()for key initerable:

d[key]=valuereturnd

fromkeys= classmethod(fromkeys)

现在可以这样创建一个字典:

>>> Dict.fromkeys("abracadabra")

{"a":None, "r":None, "b":None, "c":None, "d":None}

classmethod()使用无数据描述符协议实现:

classClassMethod(object):"Emulate PyClassMethod_Type() in Objects/funcobject.c"

def __init__(self, f):

self.f=fdef __get__(self, obj, klass=None):if klass isNone:

klass=type(obj)def newfunc(*args):return self.f(klass, *args)return newfunc

本文翻译原文地址:https://docs.python.org/2/howto/descriptor.html#id9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个基于 flask_jwt_extended 的实现销毁旧 token 的代码示例: ```python from flask import Flask from flask_jwt_extended import JWTManager, jwt_required, create_access_token, get_raw_jwt app = Flask(__name__) app.config['JWT_SECRET_KEY'] = 'super-secret' # 设置 JWT 密钥 jwt = JWTManager(app) # 模拟一个用户数据库 users = { 'user1': 'password1', 'user2': 'password2' } # 模拟一个 token 黑名单 blacklist = set() # 登录接口,返回一个新的 token @app.route('/login', methods=['POST']) def login(): username = request.json.get('username', None) password = request.json.get('password', None) if not username or not password: return {'message': 'Missing username or password'}, 400 if username not in users or users[username] != password: return {'message': 'Invalid username or password'}, 401 access_token = create_access_token(identity=username) return {'access_token': access_token}, 200 # 保护的接口,需要 token 才能访问 @app.route('/protected', methods=['GET']) @jwt_required def protected(): return {'message': 'Hello, {}!'.format(get_jwt_identity())}, 200 # 注销接口,将当前 token 加入黑名单 @app.route('/logout', methods=['DELETE']) @jwt_required def logout(): jti = get_raw_jwt()['jti'] blacklist.add(jti) return {'message': 'Successfully logged out'}, 200 # 检查 token 是否在黑名单 @jwt.token_in_blacklist_loader def check_if_token_in_blacklist(decrypted_token): jti = decrypted_token['jti'] return jti in blacklist if __name__ == '__main__': app.run() ``` 在这个示例,我们使用了 flask_jwt_extended 扩展来实现 JWT 认证和授权。用户登录时,我们生成一个新的 token 并返回给客户端。在保护的接口,我们使用 @jwt_required 装饰器来要求客户端提供有效的 token 才能访问。在注销接口,我们将当前 token 的 jti(JWT ID)加入黑名单,以便在后续的请求检查是否在黑名单。最后,我们使用 @jwt.token_in_blacklist_loader 装饰器来定义一个回调函数,用于检查 token 是否在黑名单。如果是,则拒绝访问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值