Python 中如何使用 @property

本文深入解析Python中的property属性、装饰器、类方法、静态方法及函数定义,展示如何利用这些特性增强代码功能性和安全性,特别关注于属性的动态管理。

property()

Python 解释器中内置了很多函数和类型,而 property() 即是其中一种。

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

返回 property 属性。

fget 是获得属性值的函数。fset 是设置属性值的函数。fdel 是删除属性值的函数。doc 为属性对象创建文档字符串。

一个典型的用法是定义一个托管属性 x。

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

如果 c 是 C 的实例,c.x 将调用 getx,c.x = value 将调用 setx,del c.x 将调用 delx。

如果给出 doc,doc 将给出 property 属性的文档字符串。否则该 property 将复制 fget 的文档字符串(如果存在)。将 property() 作为装饰器(decorator)创建只读的特征属性实现为:

class Parrot:
    def __init__(self):
        self._voltage = 100000

    @property
    def voltage(self):
        """Get the current voltage."""
        return self._voltage

上面的 @property 装饰器会将 voltage() 方法转化为一个具有相同名称只读属性的 “getter”,并将 voltage 的文档字符串设置为 “Get the current voltage”。

特征属性对象有 getter、setter 和 deleter 方法,它们可用作装饰器来创建该特征属性的副本,并将相应的访问函数设为所装饰的函数。示例为:

class C:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

上面的代码与之前完全等价。注意一定要给附加函数与原始的特征属性相同的名称

返回的特征属性对象同样具有与构造器参数相对应的属性 fget、fset 和 fdel。

PS:从上面的例子中好像看不出来这种从方法到属性的转换有什么用,因为不用方法也可以直接用类对象直接访问属性。但是如果考虑到数据的安全性来说,可能就需要使用这种转换了。

decotator

decorator

返回值为另一个函数的函数,通常使用 @wrapper 语法形式来进行函数变换。 

装饰器语法只是一种语法,以下两个函数定义在语义上完全等价:

def f(...):
    ...
f = staticmethod(f)

@staticmethod
def f(...):
    ...

同样概念也适用于类,但通常较少这样使用。

@classmethod

@classmethod

把一个方法封装成类方法。

一个类方法把类本身作为第一个实参,就像一个实例方法把实例本身作为第一个实参。声明如下:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...

@classmethod 形式是一个函数装饰器。它可以在类中被调用( C.f() )或者被实例被调用( C().f() )。该实例除其类外都被忽略。如果派生类对象调用类方法,则该派生类对象作为第一个参数传递。

示例:

class A():
    def func(self, x, y):
        return x * y

    @classmethod
    def cfunc(cls, x, y):
        return x * y

if __name__ == "__main__":
    print(A.func(4,5))                         # error
    print(A().func(4,5))
    print(A.cfunc(4,5))
    print(A().cfunc(4,5))

带修饰类方法:cls做为方法的第一个参数,隐式的将类做为对象,传递给方法,调用时无须实例化。

普通函数方法:self做为第一个参数,隐式的将类实例传递给方法,调用方法时,类必须实例化。

@staticmethod

@staticmethod

将方法转换为静态方法。

静态方法不会接收隐式的第一个参数。声明为:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

@staticmethod 形式是一个函数装饰器。它可以在类中被调用( C.f() )或者被实例被调用( C().f() )。该实例除其类外都被忽略。

像所有装饰器一样,也可以像常规函数一样调用 staticmethod,并对其结果执行某些操作。比如某些情况下需要从类主体引用函数并且您希望避免自动转换为实例方法。对于这些情况,使用:

class C:
    builtin_open = staticmethod(open)

 示例:

class A():
    def func(self,x,y):
        return x * y


    @staticmethod
    def sfunc(x,y):
        return x * y


if __name__=="__main__":

    print A.sfunc(6,5)

把函数嵌入到类中的一种方式,函数就属于类,同时表明函数不需要访问这个类。

Function definitions

Function definitions

函数定义就是对用户自定义函数的定义

funcdef                 ::=  [decorators] "def" funcname "(" [parameter_list] ")"
                             ["->" expression] ":" suite
decorators              ::=  decorator+
decorator               ::=  "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
dotted_name             ::=  identifier ("." identifier)*
parameter_list          ::=  defparameter ("," defparameter)* ["," [parameter_list_starargs]]
                             | parameter_list_starargs
parameter_list_starargs ::=  "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]]
                             | "**" parameter [","]
parameter               ::=  identifier [":" expression]
defparameter            ::=  parameter ["=" expression]
funcname                ::=  identifier

函数定义是一条可执行语句。 它执行时会在当前局部命名空间中将函数名称绑定到一个函数对象(函数可执行代码的包装器)。 这个函数对象包含对当前全局命名空间的引用,作为函数被调用时所使用的全局命名空间。

函数定义并不会执行函数体;只有当函数被调用时才会执行此操作。

一个函数定义可以被一个或多个装饰器表达式所包装。 当函数被定义时将在包含该函数定义的作用域中对装饰器表达式求值。 求值结果必须是一个可调用对象,它会以该函数对象作为唯一参数被发起调用。 其返回值将被绑定到函数名称而非函数对象。 多个装饰器会以嵌套方式被应用。 例如以下代码

@f1(arg)
@f2
def func(): pass

大致等价于

def func(): pass
func = f1(arg)(f2(func))

不同之处在于原始函数并不会被临时绑定到名称 func。

当一个或多个形参具有 形参 = 表达式 这样的形式时,该函数就被称为具有“默认形参值”。 对于一个具有默认值的形参,其对应的实参可以在调用中被省略,在此情况下会用形参的默认值来替代。 如果一个形参具有默认值,后续所有在 "*" 之前的形参也必须具有默认值。

默认形参值会在执行函数定义时按从左至右的顺序被求值。 这意味着当函数被定义时将对表达式求值一次,相同的“预计算”值将在每次调用时被使用。 这一点在默认形参为可变对象,例如列表或字典的时候尤其需要重点理解:如果函数修改了该对象(例如向列表添加了一项),则实际上默认值也会被修改。 这通常不是人们所预期的。 绕过此问题的一个方法是使用 None 作为默认值,并在函数体中显式地对其进行测试,例如:

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin

函数调用总是会给形参列表中列出的所有形参赋值,或用位置参数,或用关键字参数,或用默认值。 如果存在 " *identifier " 这样的形式,它会被初始化为一个元组来接收任何额外的位置参数,默认为空元组。 如果存在 " **identifier " 这样的形式,它会被初始化为一个新的有序映射来接收任何额外的关键字参数,默认为一个相同类型的空映射。 在 " * " 或 " *identifier " 之后的形参都是仅关键字形参,只能通过关键字参数传入值。

形参可以带有标注,其形式为在形参名称后加上 " : expression "。 任何形参都可以带有标注,甚至 *identifier或 **identifier 这样的形参也可以。 函数可以带有 “return” 标注,其形式为在形参列表后加上 "-> expression"。 这些标注可以是任何有效的 Python 表达式。标注的求值顺序可能与它们在源代码中出现的顺序不同。 标注的存在不会改变函数的语义。 标注值可以作为函数对象的 __annotations__ 属性中以对应形参名称为键的字典值被访问。 它们会在执行函数定义时被求值。

创建匿名函数以便立即在表达式中使用也是可能的。 这需要使用 lambda 表达式。请注意 lambda 只是简单函数定义的一种简化写法;在 “def” 语句中定义的函数也可以像用 lambda 表达式定义的函数一样被传递或赋值给其他名称。

参考资料:

1. python 官方文档:https://docs.python.org/zh-cn/3.7/

2. 博客:https://www.cnblogs.com/yhleng/p/7777843.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值