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方式,相对简洁一些

参考资料:

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

时间: 2024-08-04 03:55:43

Python: 浅淡Python中的属性(property)的相关文章

Python - 浅谈Python的编译与反编译

1 - Python编译过程涉及的文件 py 源代码文件,由python.exe解释,可在控制台下运行,可用文本编辑器进行编辑: pyc 源代码文件经过编译后生成的二进制文件,无法用文本编辑器进行编辑: 执行一个.py文件后,并不会自动生成对应的.pyc文件,需要指定触发Python来创建pyc文件: - pyc是由py文件经过编译后生成的二进制字节码(byte code)文件: - pyc文件的加载速度比py文件快: - pyc文件是一种跨平台的字节码,由python的虚拟机来执行: - py

【整理】Object-C中的属性(Property)的Setter:assign,copy,retain,weak,strong之间的区别和联系

iOS编程过程中,经常看到一些属性前面有些修饰符,比如copy,retain等. 这些关键字,是Object-C语言中,对于Property的setter. Mac官网: The Objective-C Programming Language – Declared Properties – Setter Semantics 中的解释是: Setter Semantics These attributes specify the semantics of a set accessor. They

Objective-C中的属性(property)

Objective-C中的属性(property) 它组合了新的预编译指令和新的属性访问的语法,新的属性功能显著减少了必须编写的冗长代码的数量. 下面我们来比较下面的代码 //第一种声明方法 -(void)setRainHandling:(float) rainHanding; -(float) rainHandling; -(void)setSnowHandling:(float) snowHandling; -(float) snowHandling; //第二种声明方法 @property

python学习之类和实例的属性;装饰器@property

无论是类还是实例,一切皆是对象. Python是强动态语言,和java在这点上有所不同. class Ab(): a = 666 # 定义类对象Ab,自带属性a,值为666 # 使用Ab.__dict__可以查看类Ab的属性 us1 = Ab() us2 = Ab() # 定义两个实例对象us1.us2,这两个实例自身并不具备任何属性 # 只有在__init__中定义了self.arg=xxx的情况下,实例默认会具备arg属性 动态语言中,属性自带操作方法:获取(读).设置(写):还可以定义删除

Python中,关于@property装饰器

1.为什么使用@property装饰器?br/>在类中,当我么不想在外界直接调用到类的属性,或者不想展示属性的真实内容时,可以用到@property.它规定了我们直接用 对象名.属性名 获取对象属性时并不会直接取得对象的属性,而是通过调用@property装饰过的属性函数来给调用者反馈. 2.我们为什么不使用特定的方法来进行上面的操作?原因是因为太繁琐.例: class Person: def __init__(self, username, password) -> None: self.u

浅谈python中的递归

python 浅谈 递归函数 最近在自学一些python,找了些资料.自己慢慢研究到了递归函数这一章,碰到个很经典的例子.汉诺塔的移动.一开始尝试自己写的时候发现,这东西怎么可能写的出来.但是看到别人写出来以后发现,这东西真的能写出来. 本着借鉴的目的想去分析一下别人写的东西.觉得很有意思想给大家分享一下,如果有误请大家指正首先大家可以先自己想想如何能写出来. 先说一下:所谓的递归,我认为就是不断重复调用.直到return 出当前的递归循环.在我拆分的过程中,大家不妨先自己想一下结果,然后看一下

Python 中的属性访问与描述符

在Python中,对于一个对象的属性访问,我们一般采用的是点(.)属性运算符进行操作.例如,有一个类实例对象foo,它有一个name属性,那便可以使用foo.name对此属性进行访问.一般而言,点(.)属性运算符比较直观,也是我们经常碰到的一种属性访问方式.然而,在点(.)属性运算符的背后却是别有洞天,值得我们对对象的属性访问进行探讨. 在进行对象属性访问的分析之前,我们需要先了解一下对象怎么表示其属性.为了便于说明,本文以新式类为例.有关新式类和旧式类的区别,大家可以查看Python官方文档.

python静态属性----property

1.什么是静态属性property property是一种特殊的属性,访问它的时候会执行一段功能(函数)然后返回值. 在使用者直接要某个结果的时候,就需要用到了静态属性. 2.例子 计算BMI指数. class People: def __init__(self,name,tz,hit): self.name=name self.tz=tz self.hit=hit @property def bmi(self): return self.tz / (self.hit**2) p=People(

浅谈 Python 的模块导入

浅谈 Python 的模块导入 本文不讨论 Python 的导入机制(底层实现细节),仅讨论模块与包,以及导入语句相关的概念.通常,导入模块都是使用如下语句: import ... import ... as ... from ... import ... from ... import ... as ... 一般情况下,使用以上语句导入模块已经够用的.但是在一些特殊场景中,可能还需要其他的导入方式.例如 Python 还提供了 __import__ 内建函数和 importlib 模块来实现动