Python面向对象小备忘

最近学到面向对象了,感觉到Python这方面的语法也有点神奇,这里专门归纳一下Python面向对象中 我觉得比较重要的笔记。

  • 本文目前有的内容:实例属性和类属性的访问,使用@property修饰器

Python面向对象小备忘

实例属性和类属性的访问

展开阅读

Python里面向对象编程的 类属性和实例属性与普通情况下 全局变量和局部变量还是有相似之处的:

  1. 我们可以通过实例名访问 实例属性和类属性,就像上面例子中的 new_instance.test_varnew_instance.pub_var。就像局部作用域能访问局部变量和全局变量。
  2. 我们可以通过 创建赋值让实例对象有 与类属性同名 的属性,比如 new_instance.pub_var = 'own property'就会在new_instance本身创建一个属性,从而屏蔽 通过实例名对于类属性的访问。而在没有global关键字的情况下,局部变量在局部作用域被创建赋值后也会屏蔽同名的全局变量。

对于第2点可以试试通过实例名来删除类属性:

class Test:
    pub_var = 'Hello' # 类属性

    def __init__(self):
        pass

new_instance = Test()
print(new_instance.pub_var)  # Hello
del new_instance.pub_var  # AttributeError: pub_var

很明显通过实例名是无法删除类属性 pub_var的,但如果我们给实例创建赋值一个同名属性呢?

紧接上面例子
new_instance = Test()
print(new_instance.pub_var)  # 此时访问了类属性,输出:Hello
new_instance.pub_var = 'Hello World'
print(new_instance.pub_var)  # 此时访问的是实例自身属性,输出:Hello World
del new_instance.pub_var  # 删除了实例自身属性,一切正常
print(new_instance.pub_var)  # 实例在自身找不到同名属性了,就又指向了类属性,输出:Hello
del Test.pub_var # 可以通过类名删除类属性
print(new_instance.pub_var) # 在实例自身和类里都找不到pub_var属性了,返回no attribute异常

您可以看到,您可以通过实例名称删除实例本身的属性。当实例找不到自身的属性时,它会转向查找类属性。类比局部变量和全局变量,局部变量也是在局部作用域中找到的,如果没有找到,则查找同名的全局变量。

[En]

You can see that you can delete the attributes of the instance itself through the instance name. When the instance * cannot find the attribute on itself, it will * turn to look for the class attribute * . Analogy between local variables and global variables, local variables are also found in the local scope, if not found, to find the global variable with the same name.*

通过 类名,可以在很多地方访问到类属性,并可以进行修改(比如在实例的方法函数里就可以直接通过类名访问。

Python面向对象小备忘

使用@property修饰器

展开阅读

class Test:
    def __init__(self, val):
        self.__secret_value = val

    def my_value(self):
        return self.__secret_value

new_instance = Test(233)
print(new_instance.my_value())

上面例子中我们将类 实例化为对象 new_instance (用类创建对象),该对象得到了 my_value()方法,同时Python自动调用了 __init__new_instance 绑定了属性 __value并进行赋值。

当我们要获得值的时候就要调用实例对象 new_instancemy_value()方法:

print(new_instance.my_value())

如果 使用了@property修饰器 呢?

class Test:
    def __init__(self, val):
        self.__secret1value = val

    @property
    def my_value(self):
        return self.__secret1value

new_instance = Test(233)
print(new_instance.my_value) # 末尾不用再加()了,因为这不是一个可调用的方法,而是一个属性

@property的作用正如其名,将 实例的方法转换为了 属性,上面例子中原本的方法 my_value()被修饰后只用访问对应的属性名 my_value我们就能获得同样的返回值。

这个修饰器本质上其实 仍然是对方法的调用,咱改一下上面的例子:

class Test:
    def __init__(self, val):
        self.__value = val

    @property
    def my_value(self):
        print('Here I am.') # 调用方法的时候输出'Here I am.'
        return self.__value
new_instance = Test(233) # 实例化的时候没有任何输出
print(new_instance.my_value) # 访问这个属性时实际上内部调用了my_value()的方法,因为输出了 'Here I am.' 和 233

再进一步想想, new_instance.my_value这个属性取的其实就是原本 my_value()方法的 return返回值。

接着再钻一下,原本 my_value()这个方法 只是读取了属性 __value 并返回 ,并没有进行修改。没错,这也意味着:

被@property修饰后产生的属性是只读的

Python面向对象小备忘

可以试试修改这个属性:

new_instance.my_value = 450
AttributeError: can't set attribute

很明显, my_value现在对于 new_instance而言是 只读属性。由此,在用户 不知道原方法 my_value()操作的私有属性时能起 一定的保护作用

  • 作为实例对象的一个 属性,其和方法有一定的区别,我们调用实例对象的 方法时候是 可以传参的,但属性不行,这意味着 @property修饰的方法只能有 self一个参数(否则访问属性的时候会报参数缺少的异常)。
  • 另外一个 实例对象有其他属性的,@property等修饰器修饰的方法也好,普通的实例方法也好,一定 不要和已有的属性重名。举个例子:
class Test:
    def __init__(self, val):
        self.__secret1value = val
        self.my_value = 'pre'

    @property
    def my_value(self):
        print('Here I am.')
        return self.__secret1value

new_instance = Test(233)
self.my_value='pre' -> AttributeError: can't set attribute
其实从这里还能看出来,@property修饰先于实例初始化进行,导致抛出的异常是无法修改属性值

上面我们尝试修改了@property修饰而成的属性,但返回了 can't set attribute。其实是因为咱 没有定义这个属性的写入(setter)方法.

需要修改这个@property属性的话,我们就需要请出 附赠的修饰器 @已被修饰的方法名.setter了:

class Test:
    def __init__(self, val):
        self.__secret1value = val

    @property
    def my_value(self):
        return self.__secret1value

    @my_value.setter # [被@property修饰的方法名].setter
    def my_value(self, val2set): # 这里的方法仍然是my_value
        self.__secret1value = val2set

new_instance = Test(233)
print(new_instance.my_value) # 233
new_instance.my_value = 450 # 此时这个属性有修改(setter)的方法了,我们可以修改它
print(new_instance.my_value) # 450

@property修饰的方法不同, @已被修饰的方法名.setter修饰的方法除了 self还可以接受第二个参数,接收的是 修改的值。在上面例子中我将这个形参命名为了 val2set

Python面向对象小备忘

有了阅读和写作,剩下的就是删除

[En]

With reading and writing, what is left- * delete * !

和setter类似,@property修饰器还赠有 @已被修饰的方法名.deleter修饰器,其修饰的方法和@property修饰的一样都 只接受一个参数 self

class Test:
    def __init__(self, val):
        self.__secret1value = val

    @property
    def my_value(self):
        return self.__secret1value

    @my_value.deleter # [被@property修饰的方法名].deleter
    def my_value(self):  # 注意这里只接受一个self参数
        del self.__secret1value

new_instance = Test(233)
print(new_instance.my_value)  # 233
try:
    new_instance.my_value = 450
except:
    print('Set failed.')  # Set failed.

del new_instance.my_value
print(new_instance.my_value)
AttributeError: 'Test' object has no attribute '_Test__secret1value'

这个例子中咱没有定义 my_value属性的 setter方法,所以其无法被修改。但因为定义了 deleter方法,在用 del对属性进行移除的时候会通过deleter 调用原方法,原方法中用del去删掉实例对象自己的私有属性,达成删除的目的。

总结一下 修饰器@property相关的着重点:

  1. @property让实例方法作为 属性被访问。
  2. 这一类修饰器能在 一定程度上保护实例的 私有属性不被随意修改(之所以是说一定程度上,是因为一旦用户知道了私有属性名就可以用 _类名__私有属性名进行访问,Python,很神奇吧 ( ̄ε(# ̄)☆╰╮o( ̄皿 ̄///)) 。
  3. 实例的方法名不要和 自身其他方法或属性重名。
  4. @property@已被修饰的方法名.deleter修饰的 方法只能接受 self一个参数;而 @已被修饰的方法名.setter修饰的方法除了 self外可以接受第二个参数作为被修改的值。

Python面向对象小备忘

除了 @property这种修饰器写法外,Python还提供了内置方法 property(getter,setter,deleter,doc) 来达成相同的效果:

class Test:
    pub_var = 'Hello'

    def __init__(self, val):
        self.__secret1value = val
        self.test_val = 'World'

    def __getter(self):
        return self.__secret1value

    def __deleter(self):
        del self.__secret1value

    my_value = property(__getter, None, __deleter)

new_instance = Test(233)
print(new_instance.test_var) # World (通过实例名访问实例属性)
print(Test.pub_var)  # Hello (尝试通过类名访问类属性)
print(new_instance.pub_var)  # Hello (尝试通过实例访问类属性)
print(Test.my_value) #  (这个其实也是类属性,通过类名能访问到)
print(new_instance.my_value)  # 233 (通过实例名访问类属性,间接调用了__getter,绑定上了self

property(getter,setter,deleter,doc)接受的四个参数分别为 读方法写方法删方法描述信息,这四个参数都是可以留空的,当 getter也留空时访问这个属性会提示 unreadable attribute

Python面向对象小备忘

通过上面的例子可以看出, property方法返回的是 类属性,而 实例对象是可以访问到类属性的,所以当我们访问 new_instance.my_value的时候就是在绑定实例的基础上访问getter方法,但一旦对 new_instance.my_value属性进行 写或删操作后就给 new_instance自身创建了一个属性 my_value,再访问就不是指向类属性了。(详细看实例属性和类属性的访问 )

再回去看实例属性和类属性的访问,加上这个内置方法 property(),于是就有了奇妙的骚操作:

class Test:
    def __init__(self, val):
        Test.test_var = property(lambda self: val) # 闭包写法

new_instance = Test(233)
print(new_instance.test_var) # 233
  1. 这个操作中首先利用了一个匿名函数 充当getter方法,传入 property第一个参数,然后property会返回一个类属性。
  2. 因为在实例方法里我们也能访问到类名,于是我们将这个property类属性赋值给 Test.test_vartest_var便是一个名副其实的类属性了。
  3. 通过实例名 new_instance能访问到类属性 test_var
  4. 之前的这个例子可以看出,当我们通过类名访问property属性时 只会返回一个property object,但是通过 已创建的实例对象来访问就能 间接调用getter方法。
  5. 在上面过程中,始终没有 new_instance自身属性出现,取而代之我们利用 闭包机制保护了创建实例时传入的值,我们 完全无法通过实例名 修改或者 删除 test_var这个属性,真正将其保护起来了。

Python面向对象小备忘

当然,别让用户知道了 类名,不然一句 Test.test_var = xxx直接破防(,,#゚Д゚)。

To be updated……

Original: https://www.cnblogs.com/somebottle/p/python_oop_note.html
Author: SomeBottle
Title: Python面向对象小备忘

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/510686/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球