zoukankan      html  css  js  c++  java
  • Python: 浅淡Python中的属性(property)

    起源:
    项目过程中需要研究youtube_dl这个开源组件,翻阅其中对类的使用,对比c#及Delphi中实现,感觉Python属性机制挺有意思。
    区别与高级编程语言之单一入口,在类之属性这一方面,它随意的太多,以致于习惯了高级语言的严谨,对如此随意心里倒是有些不安。
    也难怪,因为其数据类型弱限制性,往往一个函数返回了一个结果,追溯此返回值类型,有时需要费上许多工夫!
    我不是随意的人,但随意起来我还真不是人,用在此处,颇为贴切:b

    属性,是对事物某种特性的抽象,面向对象编程中一个重要概念;区别于字段,它通常表示为字段的扩展,加以访问与设置保护机制。
    比如动物,它的颜色、重量,都可以说是它的属性,此篇以一动物类来做例子,浅探其中属性机制。

    1、新式类和经典类(New-style classes)

    Python 2.x默认类为经典类,而对属性支持完全者为新式类,其区别请自行度娘。
    Python 3.x默认即为新式类,不必显式继承于object类。
    如下面代码类之定义:

    #此为经典类
    class AnimalClassic:
        pass
    
    #此为新式类,Python 2.2始支持
    class Animal(object):
        pass

    虽一object差别,但其内在机制,改变颇多。略一窥之,大体如下:

    >>>print dir(AnimalClassic) 

    返回:

    ['__doc__', '__module__']

    >>>print dir(Animal)

    返回:

    ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

    由此可知,其加入了诸多面向对象的支持;此节属性知识,亦基于新式类构建。所以诸君用Python 2.x做开发,写面向对象代码时,切记要继承于object类

    2、随意的属性

    通常,我们这样定义属性:

    class Animal(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age

    即在实例初始化时,__init__函数中赋值,其中值可为实例访问:

    a = Animal('black dog', 3)
    print 'Name:', a.name
    print 'Age:', a.age
    a.color = 'Black'
    print 'Color:', a.color

    结果为:

    Name: black dog
    Age: 3
    Color: Black

    看到了吧?运行期,能动态为实例添加属性,比如color。但此方法所添加,只能作用于此实例,而无影响于类
    怎么限制它?
    用__slots__这个东西。比如,__slots__ = ['name', 'age'],则类只能添加这两个属性,蛋疼,不好用

    如果想要在运行期给类添加属性,要用到MethodType这东西,例如:

    def set_color(self, color):
        self.color = color
    Animal.set_color = MethodType(set_color, None, Animal)
    
    a1 = Animal('yellow dog', 3)
    a1.set_color('Yellow')
    print a1.color

    ……喔,有点凌乱,还是不好用!或者说,不好记,心里没谱儿。


    3、@property(推荐)

    习惯了高级语言的严谨,总想对属性加以访问控制,相对安全些,比如直接在__init__中定义公用属性,从封装性来说,它是不好的写法。
    属性之访问,它亦有机制,其一便是@propery关键字。用此关键字,其获取、设置函数,须与属性名一致。
    @property可以把一个实例方法变成其同名属性,以支持.号访问,它亦可标记设置限制,加以规范,如下代码:

    class Animal(object):
        def __init__(self, name, age):
            self._name = name
            self._age = age
            self._color = 'Black'
    
        @property
        def name(self):
            return self._name
    
        @name.setter
        def name(self, value):
            if isinstance(value, basestring):
                self._name = value
            else:
                self._name = 'No name'
    
        @property
        def age(self):
            return self._age
    
        @age.setter
        def age(self, value):
            if value > 0 and value < 100:
                self._age = value
            else:
                self._age = 0
                # print 'invalid age value.'
    
        @property
        def color(self):
            return self._color
    
        @color.setter
        def color(self, value):
            self._color = value;
        
    
    a = Animal('black dog', 3)
    a.name = 'white dog'
    a.age = 300
    print 'Name:', a.name
    print 'Age:', a.age

    这样在设定值时候,总算有个判断取舍,是不是好一些?
    私有变量以_开头,是种编码约定,当然也可以直接访问它。
    不过既如此写,直接访问是不推荐的。
    若真要为私有变量,则加双下划线,比如__name,也一样阻止不了访问,但让我们知道,它不想被直接用到,如下代码:

    class Animal(object):
        def __init__(self, name):
            self.__name = name
    
    a = Animal('black dog')
    print a._Animal__name

    这种写法当真好奇怪!
    引廖雪峰言:总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。自觉靠谱么?不见得,但愿做人靠谱。

    4、property函数
    它以一个函数形式,定义一个属性,与@property实现原理类似,或者就是它的的变异用法。
    其原型为:

    property(fget=None, fset=None, fdel=None, doc=None)

    譬如上面Animal类,其可用此改为:

    class Animal(object):
        def __init__(self, name, age):
            self._name = name
            self._age = age
            self._color = 'Black'
    
        def get_name(self):
            return self._name
    
        def set_name(self, value):
            if isinstance(value, basestring):
                self._name = value
            else:
                self._name = 'No name'
    
        name = property(fget=get_name, fset=set_name, fdel=None, doc='name of an animal')
    
        def get_age(self):
            return self._age
    
        def set_age(self, value):
            if value > 0 and value < 100:
                self._age = value
            else:
                self._age = 0
                # print 'invalid age value.'
    
        age = property(fget=get_age, fset=set_age, fdel=None, doc='name of an animal')
        
    
    a = Animal('black dog', 3)
    a.name = 'white dog'
    a.age = 3
    print 'Name:', a.name
    print Animal.name.__doc__
    print 'Age:', a.age

    其输出结果一样,看来只是写法不同。

    后记:
    由此可见,Python作为一种解释型语言,其简单易用,但其随意性也大。如此属性篇,也只是阐述其中一点,其它方面须在使用中去学习留意。
    习惯了语法严谨,编码亦请以Pythonic的风格去编写,python实在没有多大限制,只要正确计算机都能够编译,代码风格是给人看的,优雅总比零乱好。

    如上面几种属性写法,我尤其喜欢@property方式,相对简洁一些。而此写法,亦为youtube-dl推荐样式,着实不错。

     

    参考资料:

    Built-in Functions
    Python基础:新式类的属性访问
    Python深入03 对象的属性

  • 相关阅读:
    【算法】HashMap相关要点记录
    【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)
    SpringCloud Openfeign Get请求服务传递对象的报400 Post not support的错误解决办法
    掌握 Promise 的逻辑方法
    JavaScript的执行上下文,真没你想的那么难
    一套标准的ASP.NET Core容器化应用日志收集分析方案
    在IIS中部署前后端应用,多么痛的领悟!
    吐槽一下Abp的用户和租户管理模块
    ant-design-vue中tree增删改
    微服务下的注册中心如何选择
  • 原文地址:https://www.cnblogs.com/crwy/p/6852347.html
Copyright © 2011-2022 走看看