python中名称修饰与描述符

名称修饰

java和C#等其他高级语言中都有private关键字来修饰一个属性或字段是私有的,但是python中并没有private,而是有个与它接近的概念旧式名称修饰。每当在一个属性前面加上__前缀,解释器就会立刻将其重命名:

直接访问会抛异常

利用dir函数查看内部属性

python内部会把__前缀的属性重命名为【_类名+属性名】;因此在python中如果一个属性不是共有的就约定使用双下划线__为前缀,它不会调用任何名称修饰的算法,只是说名这个属性是该类的私有属性。

幸运的是python中还有其他机制类构建类的私有和共有代码,即使用描述符和property这些POP设计的关键特性

什么是描述符

python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有 __ get__(), __ set__(), 和__delete__()。如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符

它是一个类,定义了另一个类的访问方式,即一个类可以将属性管理托管给另一个类

描述符协议

描述符类基于3个特殊方法,这3个方法组成了描述符协议

  • __ set__(self,obj,type=none)设置属性时调用,也称为setter
  • __ get__(self,obj,value) 读取属性时调用,也称为getter
  • __ delete__(self,obj) 对象调用del时调用这一方法

数据描述符

实现了__get__()和__set__()的描述符称为数据描述符

非数据描述符

只实现了__get__()

属性查找顺序

在每次查找属性时,描述符协议的方法实际上是有对象的特殊方法__getattribute__()调用(不是__getattr__()),每次通过点号(instance.attribute)或者getattr(instance,‘attribute‘)函数来执行查找时,都会隐式的调用__getattribute__(),它按下列属性查找该属性:

①.实例属性

②.类属性

③.父类属性

④.__ getattr__()方法

从以上代码可以看出,实例t的属性并不包含cls_val,cls_val是属于类Test的

描述符3个特殊方法,即魔法方法:__ get__(), __ set__(), __ delete__()

魔法方法__ get__(), __ set__(), __ delete__()的原型为

  • __ get__(self, instance, owner)
  • __ set__(self, instance, value)
  • __ del__(self, instance)

原型中self,instance,owner分别代表什么意思呢?还是用实例代码来演示:

class Descriptors:
        """
        数据描述符
        """
        def __init__(self, key, expected_type):
            """
            key:          用户传进的值
            expected_type:用户传进的值的类型
            """
            self.key = key
            self.expected_type = expected_type

        """
        描述符的三个内置属性的参数如下:
        ---------------------------------------------------
        self:     是描述符的对象,不是使用描述符类的对象
        instance: 这才是使用描述符类的对象
        owner:    是instance的类
        value:    是instance的值
        ---------------------------------------------------
        """

        def __get__(self, instance, owner):
            print("执行Descriptors的get")
            return instance.__dict__[self.key]    #将参数存入实例的字典

        def __set__(self, instance, value):
            print("执行Descriptors的set")
            #如果用户输入的值和值的类型不一致,则抛出TypeError异常
            if not isinstance(value, self.expected_type):
                raise TypeError("参数%s必须为%s"%(self.key, self.expected_type))
            instance.__dict__[self.key] = value   #为实例字典的key设值

        def __delete__(self, instance):
            print("执行Descriptors的delete")
            instance.__dict__.pop(self.key)       #删除实例字典的key

    class Light:

        #使用描述符
        name = Descriptors("name",str)
        price = Descriptors("price",float)

        def __init__(self, name, price):
            self.name = name
            self.price = price

    #设置两个参数,触发两次set的执行
    light = Light("电灯泡", 66.66)
    print(light.__dict__)
    light.name = "火箭筒"
    print(light.__dict__)
    """
    执行Descriptors的set
    执行Descriptors的set
    {‘name‘: ‘电灯泡‘, ‘price‘: 66.66}
    执行Descriptors的set
    {‘name‘: ‘火箭筒‘, ‘price‘: 66.66}
    """

描述符的种类及优先级

描述符分为数据描述符和非数据描述符。把至少实现了内置属性__set__()和__get__()方法的描述符称为数据描述符;把实现了除__set__()以外的方法的描述符称为非数据描述符。之所以要区分描述符的种类,主要是因为它在代理类属性时有着严格的优先级限制。例如当使用数据描述符时,因为数据描述符大于实例属性,所以当我们实例化一个类并使用该实例属性时,该实例属性已被数据描述符代理,此时我们对该实例属性的操作是对描述符的操作。描述符的优先级的高低如下:

类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()

类属性>数据描述符

在以下实验中,使用 Light.name = "电灯泡" 语句没有触发set的执行说明类属性的优先级大于数据描述符的优先,此时相当于类属性覆盖了数据描述符,从而说明对类属性的一切操作都与描述符无关

class Descriptors:
        """
        数据描述符
        """
        def __get__(self, instance, owner):
            print("执行Descriptors的get")
        def __set__(self, instance, value):
            print("执行Descriptors的set")
        def __delete__(self, instance):
            print("执行Descriptors的delete")

    class Light:
        #使用描述符
        name = Descriptors()

    #测试
    Light.name               #执行描述符的get内置属性
    print(Light.__dict__)    #此时的name显示的是描述符的对象
    Light.name = "电灯泡"     #没有执行描述符的set内置属性
    print(Light.name)        #输出:电灯泡
    del Light.name           #没有执行描述符的delete内置属性
    print(Light.name)        #报错,因为Light类中的name被删了

数据描述符 > 实例属性

在以下实验中,数据描述符的优先级大于实例属性的优先级,此时实例属性name被数据描述符所覆盖,而price没有描述符代理,所以它任然是实例属性

class Descriptors:
        """
        数据描述符
        """
        def __get__(self, instance, owner):
            print("执行Descriptors的get")
        def __set__(self, instance, value):
            print("执行Descriptors的set")
        def __delete__(self, instance):
            print("执行Descriptors的delete")

    class Light:

        #使用描述符
        name = Descriptors()

        def __init__(self, name, price):
            self.name = name
            self.price = price

    #使用类的实例对象来测试
    light = Light("电灯泡",60)       #执行描述符的set内置属性
    light.name                      #执行描述符的get内置属性
    print(light.__dict__)           #查看实例的字典,不存在name
    print(Light.__dict__)           #查看类的字典,存在name(为描述符的对象)
    del light.name                  #执行描述符的delete内置属性

实例属性 >非 数据描述符

在以下实验中,如果我们的实例属性中使用了非数据描述符,就不能对其进行复制操作。可见非数据描述符应该应用于不需要设置值的属性或者函数中。上述的设计没有多大的意义,只是增加对描述符的理解

class Descriptors:
        """
        非数据描述符
        """
        def __get__(self, instance, owner):
            print("执行Descriptors的set")
        def __delete__(self, instance):
            print("执行Descriptors的delete")

    class Light:

        #使用描述符
        name = Descriptors()

        def __init__(self, name, price):
            self.name = name
            self.price = price

    #测试
    light = Light("电灯泡",60)   #报错,描述符中没有__set__()方法

经以下实验证明在该类中并没有set方法,所以该类是一个非数据描述符,Python中一切皆对象,函数也是一个对象,既然是对象那也可以是类实例化所得到的结果。函数在类中本身也是一种属性(函数属性),描述符在应用的时候也是被实例化为一个属性

class Descriptors:
        """
        非数据描述符
        """
        def func(self):
            print("世界的变化真快!近日00后都已经开始在街头打小三了")

    d = Descriptors()
    d.func()
    print(hasattr(Descriptors.func,"__set__"))     #False
    print(hasattr(Descriptors.func,"__get__"))     #True
    print(hasattr(Descriptors.func,"__delete__"))  #False
    d.func = "函数也是属性,也可以赋值,没毛病"
    print(d.func)
    del d.func
    d.func

描述符模拟系统的内置属性及

此利用描述符的原理,我们完全可以自定义模拟@classmethod、@staticmethd、@property、等属性。实现这种类似系统的属性,我们还需要装饰器作为修饰,结合装饰器做成一个描述符。下面将简单的介绍使用描述符模拟系统自带的装饰器。

1.模拟 @classmethod

class Imitate_classmethod:
        """
            使用描述符模拟@classmethod
    """
        def __init__(self, func):
            self.func = func

        def __get__(self, instance, owner):
            #对传进函数进行加工,最后返回该函数
            def machining_func(*args, **kwargs):
                print("函数加工处理后,返回实例的类")
                return self.func(owner, *args, **kwargs)
            return machining_func

    class Test:

        book_name = "从你的世界路过"

        #使用装饰器的结果等价于将函数变为属性:
        # book = Imitate_classmethod( book )
        @Imitate_classmethod
        def book_1(cls):
            print("这本书的名字是:%s"%cls.book_name)

        @Imitate_classmethod
        def book_2(cls, price):
            print("这本书的名字是:%s;价格是 %s"%(cls.book_name, price))

    #测试
    Test.book_1()
    Test.book_2(28.5)
    """
    运行输出:
    ---------------------------------------------
    函数加工处理后,返回实例的类
    这本书的名字是:从你的世界路过
    函数加工处理后,返回实例的类
    这本书的名字是:从你的世界路过;价格是 28.5
    ---------------------------------------------
    """

  

2.模拟 @staticmethod

  staticmethod方法与classmethod方法的区别在于classmethod方法在使用需要传进一个类的引用作为参数。而staticmethod则不用。

class Imitate_staticmethod:
        """
        使用描述符模拟@staticmethod
        """
        def __init__(self, func):
            self.func = func

        def __get__(self, instance, owner):
            #对传进函数进行加工,最后返回该函数
            def machining_func(*args, **kwargs):
                print("函数加工处理后,返回实例的类")
                return self.func(*args, **kwargs)
            return machining_func

    class Test:

        @Imitate_staticmethod
        def static_func(*args):
            print("您输入的是:",*args)

    #测试
    Test.static_func("柠檬","香蕉")
    test = Test()
    test.static_func(110, 112)
    """
    运行输出:
    -------------------------------------------------------------------
    函数加工处理后,返回实例的类
    您输入的是: 柠檬 香蕉
    函数加工处理后,返回实例的类
    您输入的是: 110 112
    -------------------------------------------------------------------
    """

3.模拟 @property

  在以下实验中,我们将描述符的回调结果存入对象字典中的好处是我们以后再执行函数时就不会每一次都触发描述的运行,从而提高程序的效率。这样,我们有再执行同样的函数时,解释器会先检索对象的字典,如果字典存在上次执行结果的值,那就不用触发描述符的运行了。在这个实验中必须强调的一点是描述符的优先级,我们想让程序的描述符不能覆盖实例属性就必须使用非数据描述符。所以因需求不同,描述符的优先级也不同

class Imitate_property:
        """
        使用描述符模拟property
        """
        def __init__(self, func):
            self.func = func

        def __get__(self, instance, owner):
            if instance is None:
                return self
            #回调传入的函数,将运行结果保存在变量res中
            res = self.func(instance)
            #为函数名func.__name__设置一个值res后存入对象的字典中
            setattr(instance, self.func.__name__, res)
            return res

    class Test:

        def __init__(self, value):
            self.value = value

        @Imitate_property
        def function(self):
            return self.value**2

    test = Test(2)
    print(test.function)  #输出:4
    print(test.__dict__)  #输出:{‘value‘: 2, ‘function‘: 4}
    print(test.function)  #输出:4
    print(test.function)  #输出:4
    print(test.__dict__)  #输出:{‘value‘: 2, ‘function‘: 4}

系统内置属性

此利用描述符的原理,我们完全可以自定义模拟@classmethod、@staticmethd、@property、等属性。实现这种类似系统的属性,我们还需要装饰器作为修饰,结合装饰器做成一个描述符。下面将简单的介绍使用描述符模拟系统自带的装饰器

首先介绍一些这三个属性都是干什么用的:

首先,类的一般来说需要实例化后才能调用。但是使用了这前面两个装饰器,就可以不用实例化就可以直接调用类了。

直接 类名.方法名() 来调用。这样有助于组织代码。

但是他们两个还是有区别的。

  • @staticmethod 不需要表示自身对象的self和自身类的cls参数,就和使用普通的函数一样。即在类里有@staticmethod的,可以直接调用,没有加上改装饰器的方法就不能去调用
  • @classmethod 不需要self参数,但是第一个参数需要表示自身类的cls参数。即有@classmethod的话,在类里的所有方法都可以调用的

用实例来验证他们两的区别:

class A(object):
  def foo(self,x):
    print "executing foo(%s,%s)"%(self,x)

  @classmethod
  def class_foo(cls,x):
    print "executing class_foo(%s,%s)"%(cls,x)

  @staticmethod
  def static_foo(x):
    print "executing static_foo(%s)"%x 

a=A()

上述类有三个函数,使用如下:

a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)

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

a.class_foo(1)
# executing class_foo(<class ‘__main__.A‘>,1)

A.class_foo(1)
# executing class_foo(<class ‘__main__.A‘>,1)

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

a.static_foo(1)
# executing static_foo(1)

A.static_foo(‘hi‘)
# executing static_foo(hi)

  

通过上述验证我们发现:

foo()的调用者必须是类A的一个实例,class_foo()与static_foo()的调用者既可以是类也可以是某个实例

参数不同,foo() 参数为self和其他参数,class_foo()参数使用类(cls)替换了self,static_foo()则只有参数,没有self和类(cls)

a.foo(1)中的foo()与a是绑定的,class_foo()是与类绑定的,而static_foo()与这两者都没有绑定

@property

作用:

将类方法转换为只读属性

重新实现一个属性的setter和getter方法

原文地址:https://www.cnblogs.com/xxpythonxx/p/12569515.html

时间: 2024-10-04 21:18:48

python中名称修饰与描述符的相关文章

Python中的动态属性与描述符

动态属性与属性描述符 属性描述符是什么? ??在解释属性查找顺序之前我们需要了解Python中的属性描述符,属性描述符作为其他类对象的属性而存在,实现了特殊方法中的get.set.delete中的一种即可称作属性描述符. 其中只实现了__get__()的称作非数据描述符,实现了__get__()和__set__()方法的称作数据描述符. Data.py class Data(): def __get__(self, instance, owner): pass def __set__(self,

python进程间传递文件描述符扩展库

由于python本身的线程基本上比较残废,所以为了利用机器的cpu,就不得不用上多进程... 在游戏服务器的设计中,最为常见的方式是: 挂一个前端服务器,专门来维护与客户端的连接,然后将客户端的请求数据转发给后端服务器... 上面的方式是现在最为正统的... 但是自己因为环境的限制,需要做到对客户端透明,然后将后端的服务器转换成为多进程的...所以这里就只有用一点比较别扭的方法了,首先处理登录等一些常规的逻辑放在前端服务器,当进入放进进行匹配战斗之后,将客户端的socket连接直接交给后端服务器

Windows系统调用中的系统服务表描述符

 Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html Windows系统调用中的系统服务表描述符 在前面,我们将解过 系统服务表.可是,我们有个疑问,系统服务表存储在哪里呢? 答案就是:系统服务表 存储在 系统服务描述符表中.(其又称为 SSDT Service Descriptor Table)  一.使用PELord函数从ntoskrnl.exe文件中查看SSDT导出函数 如图,可以看出KeServiceDes

关于Java Web应用中的配置部署描述符web.xml

一.web.xml概述 位于每个Web应用的WEB-INF路径下的web.xml文件被称为配置描述符,这个 web.xml文件对于Java Web应用十分重要,每个Java Web应用都必须包含一个web.xml文件,且必须放在WEB-INF路径下. 对于Java Web应用而言,WEB-INF是一个特殊的文件夹,Web容器会包含该文件夹下的内容,客户端浏览器无法访问WEB-INF路径下的任何内容.Java Web应用的绝大部分内容都由web.xml文件来配置管理.我们后面介绍的如下内容都要通过

Python的特性(Property)和描述符

特性(Property) 特性是对类的一个特定属性进行拦截,在操作这个属性时,执行特定的函数,对属性的操作进行拦截. 特性的实现 特性使用property类来实现,也可以使用property装饰器实现,二者本质是一样的. property类的__init__函数接收4个参数,来实现属性的获取.赋值.删除及文档. def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of proper

Python中readline()函数 去除换行符

从Python中readline()函数读取的一行内容中含有换行符\n,很多时候我们需要处理不含有换行符的字符串,此时就要去掉换行符\n. 方法是使用strip()函数. 例子如下: f = open("./DownloadList.txt") line = f.readline() while line: line = line.strip('\n') line = f.readline() f.close() 原文地址:https://www.cnblogs.com/lehoho/

python中的 @ 修饰符

今天学习廖老师的python教程,碰到了修饰符'@',不太了解,查看了下官方文档. 简单的整理下: @dec2 @dec1 def func(arg1, arg2, ...): pass 等价于 def func(arg1, arg2, ...): pass func = dec2(dec1(func)) 使用示例: 在comp.lang.python 和 python-dev的大部分讨论集中在更简捷地使用内置修饰符staticmethod() 和 classmethod() 上.但修饰符的功能

python中的修饰符@的作用

1.一层修饰符 1)简单版,编译即实现 在一个函数上面添加修饰符 @另一个函数名 的作用是将这个修饰符下面的函数作为该修饰符函数的参数传入,作用可以有比如你想要在函数前面添加记录时间的代码,这样每个函数调用时就能够知道是什么时候调用的,但是你不想手动地去给每个函数添加,那么就能够使用该修饰符实现这样的功能,下面举例说明: #coding=UTF-8 import time def showTime(fn): print('calling time : ', time.time()) fn() #

python描述符descriptor(二)

python内置的描述符 python有些内置的描述符对象,property.staticmethod.classmethod,python实现如下: class Property(object): def __init__(self,getf,setf,delf,doc): self.getf=getf self.setf=setf self.delf=delf self.doc=doc def __set__(self,instance,own=None): if instance is N