python描述器有什么用_9.描述器

目录:

1.描述器的表现

2.描述器定义

3.属性的访问顺序

4.Python中的描述器

5.新增方法

1. 描述器的表现

用到3个魔术方法:

1.__get__(),object.__get__(self,instance,owner)

2.__set__(),object.__set__(self,instance,value)

3.__delete__(),object.__delete__(self,instance)

self指代当前实例,调用者

instance是owner的实例

owner是属性的所属的类

例1:

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

class B:

x = A()

def __init__(self):

print('B.init')

print('****************')

print(B.x.a1)

print('----------------')

b = B()

print(b.x.a1)

# 运行结果

A.init

****************

a1

----------------

B.init

a1

类加载的时候,类变量需要先生成,而类B的x属性是A的实例,所以类A先初始化,所以打印A.init。

然后执行到打印B.x.a1,然后实例化并初始化B的实例b。

打印b.x.a1,会查到类属性b.x,指向A的实例,所以返回A实例的属性a1的值。

例2:

根据例1中的执行流程,在类A中添加get方法

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

def __get__(self, instance, owner):

print("A.__get__ {} {} {}".format(self,instance,owner))

class B:

x = A()

def __init__(self):

print('B.init')

print('****************')

print(B.x)

print('----------------')

b = B()

print(b.x)

# 执行结果

A.init

****************

A.__get__ <__main__.a object at> None

None

----------------

B.init

A.__get__ <__main__.a object at> <__main__.b object at>

None

因为定义了__get__方法,类A就是一个描述器,对类B或者类B的实例的x属性读取,成为对类A的实例的访问,就会调用__get__方法。

例3:

如何解决例2中报错问题,因为__get__方法,self,instance,owner三个参数?

__get__(self, instance, owner)

B.x调用返回<__main__.a object at> None

b.x调用返回<__main__.a object at> <__main__.b object at>

1.self对应都是A的实例

2.owner对应都是B类

3.instance

-None表示不是B类的实例,对应调用B.x

-<__main__.b object at>表示是B的实例,对应调用B().x使用返回值解决。返回self,就是A实例,该实例有a1属性,返回正常。

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

def __get__(self, instance, owner):

print("A.__get__ {} {} {}".format(self,instance,owner))

return self

class B:

x = A()

def __init__(self):

print('B.init')

print('****************')

print(B.x)

print(B.x.a1)

print('----------------')

b = B()

print(b.x)

print(b.x.a1)

# 运行结果

A.init

****************

A.__get__ <__main__.a object at> None

A.__get__ <__main__.a object at> None

a1

----------------

B.init

A.__get__ <__main__.a object at> <__main__.b object at>

A.__get__ <__main__.a object at> <__main__.b object at>

a1

例4:

类B的属性也可以这样吗?

只有类属性是类的实例才可以

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

def __get__(self, instance, owner):

print("A.__get__ {} {} {}".format(self,instance,owner))

return self # 解决返回值为None的问题

class B:

x = A()

def __init__(self):

print('B.init')

self.b = A() # 实例属性也指向一个A的实例

print('****************')

print(B.x)

print(B.x.a1)

print('----------------')

b = B()

print(b.x)

print(b.x.a1)

print(b.b) # 并没有触发__get__

# 运行结果

A.init

****************

A.__get__ <__main__.a object at> None

A.__get__ <__main__.a object at> None

a1

----------------

B.init

A.init

A.__get__ <__main__.a object at> <__main__.b object at>

A.__get__ <__main__.a object at> <__main__.b object at>

a1

2. 描述器定义

Python中,一个类实现了__get__,__set__,__delete__三个方法中的任何一个方法,就是描述器。实现这三个中的某些方法,就支持了描述器协议。

如果一个类的类属性设置为描述器实例,那么它被称为owner属主。

当该类的该类属性被查找、设置、删除时,就会调用描述器相应的方法。

仅实现了__get__,就是非数据描述符non-data descriptor

实现了__get__、__set__就是数据描述符data descriptor

3. 属性的访问顺序

例1:

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

def __get__(self, instance, owner):

print("A.__get__ {} {} {}".format(self,instance,owner))

return self

class B:

x = A()

def __init__(self):

print('B.init')

self.x = 'b.x' # 增加实例属性x

print('****************')

print(B.x)

print(B.x.a1)

print('----------------')

b = B()

print(b.x)

print(b.x.a1) # AttributeError: 'str' object has no attribute 'a1'

# 执行结果

Traceback (most recent call last):

File "C:/Users/dell/PycharmProjects/pythonProject/test2.py", line 23, in

print(b.x.a1)

AttributeError: 'str' object has no attribute 'a1'

A.init

****************

A.__get__ <__main__.a object at> None

A.__get__ <__main__.a object at> None

a1

----------------

B.init

b.x

例2:

上例中,类A只实现了__get__()方法,b.x访问到了实例的属性,而不是描述器。

class A:

def __init__(self):

self.a1 = 'a1'

print('A.init')

def __get__(self, instance, owner):

print("A.__get__ {} {} {}".format(self,instance,owner))

return self

def __set__(self, instance, value):

print('A.__set__ {} {} {}'.format(self,instance,value))

self.data = value

class B:

x = A()

def __init__(self):

print('B.init')

self.x = 'b.x'

print('****************')

print(B.x)

print(B.x.a1)

print('----------------')

b = B()

print(b.x)

print(b.x.a1)

print(b.x.data)

# 运行结果

A.init

****************

A.__get__ <__main__.a object at> None

A.__get__ <__main__.a object at> None

a1

----------------

B.init

A.__set__ <__main__.a object at> <__main__.b object at> b.x

A.__get__ <__main__.a object at> <__main__.b object at>

A.__get__ <__main__.a object at> <__main__.b object at>

a1

A.__get__ <__main__.a object at> <__main__.b object at>

b.x

NOTE:

1.所有b.x就会访问描述器的__get__()方法,代码中返回的self就是描述器实例,它的实例字典中就保存着a1和data属性,可以打印b.x.__dict__就可以查看

2.属性查找顺序:实例的__dict__优先于非数据描述器,数据描述器优先于实例的__dict__

3.__delete__方法有同样的效果,有了这个方法,也是数据描述器

4. Python中的描述器

Python的方法(包括staticmethod()和classmethod())都实现为非数据描述器,因此可以重新定义和覆盖方法。这允许单个实例获取与同一类的其他实例不同的行为。

property()函数实现为一个数据描述器,因此,实例不能覆盖属性的行为。

例1:

class A:

@classmethod

def foo(cls):

pass

@staticmethod

def bar():

pass

@property

def z(self):

return 5

def getfoo(self):

return self.foo

def __init__(self):

self.foo = 100

self.bar = 200

a = A()

print(a.__dict__)

print(A.__dict__)

# 运行结果,foo,bar都可以实例中覆盖,但z不可以

{'foo': 100, 'bar': 200}

{

'__module__': '__main__',

'foo': ,

'bar': ,

'z': ,

'getfoo': ,

'__init__': ,

'__dict__': ,

'__weakref__': ,

'__doc__': None

}

5. 新增方法

新增描述器方法set_name,它在属主类构建的时候就会调用

class A:

def __init__(self):

print('A init')

def __get__(self, instance, owner):

print(1,self,instance,owner)

return self

def __set_name__(self, owner, name):

print(2,self,owner,name)

self.name = name

class B:

x = A()

print('**********')

print(B().x)

# 运行结果,此方法是为了知道属主和属主类的类属性名

A init

2 <__main__.a object at> x

**********

1 <__main__.a object at> <__main__.b object at>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值