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

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

实例属性和类属性的访问

展开阅读

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异常

可以看出通过实例名可以删除实例自身的属性,当实例在自身上找不到属性时,就会转而寻找类属性。类比局部变量和全局变量,局部变量也是先在局部作用域找,如果没找到就去找同名的全局变量。

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

使用@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修饰后产生的属性是只读的

可以试试修改这个属性:

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

有了读和写,还差什么呢——

和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外可以接受第二个参数作为被修改的值。


除了@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) # <property object at 0x0000025990BC5770> (这个其实也是类属性,通过类名能访问到)
print(new_instance.my_value) # 233 (通过实例名访问类属性,间接调用了__getter,绑定上了self

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

通过上面的例子可以看出,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这个属性,真正将其保护起来了。

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

To be updated......

Python面向对象小备忘的更多相关文章

  1. Python 编程小备忘

    1. 获取当前日期,或者间隔当前任意天的日期. >>> import datetime>>> print (datetime.date.today()-dateti ...

  2. python常用库函数 - 备忘

    基础库 1. 正则表达式:re 符号 ()小括号 -- 分组 []中括号 -- 字符类,匹配所包含的任一字符 #注:字符集合把[]里面的内容当作普通字符!(-\^除外) {}大括号 -- 限定匹配次数 ...

  3. python之正则表达式备忘

    一简介:就其本质而言,正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译成一系列的字节码,然后由用 C ...

  4. Python常用方法库备忘(一)_当前路径下文件夹和文件

    #!/usr/bin/env python # -*- coding:utf-8 -*- # --------------*-------------- # @Author : AilF # @Tim ...

  5. python面向对象小tips

    (一).python鸭子类型 python作为动态语言继承和多态与静态语言(像java)有很大的不同:比如说在java中的某个方法,如果传入的参数是Animal类型,那么传入的对象必须是Animal类 ...

  6. python面向对象小练习

    就是几个动物,自动排列生成什么的 class Animal(object): def __init__(self,name,weight): self.name = name self.weight ...

  7. Python 装饰器备忘

    def deco(attr): ''' 装饰器,共包含三层返回结构 \n 第一层:用于接收 @deco 的参数,此处的代码只在初始化装饰器时执行一次 \n 第二层:用于接收 function,此处的代 ...

  8. python - opencv 的一些小技巧备忘

    python - opencv 的一些小技巧备忘 使用python-opencv来处理图像时,可以像matlab一样,将一幅图像看成一个矩阵,进行矢量操作,以加快代码运行速度. 下面记录几个常用的操作 ...

  9. python序列,字典备忘

    初识python备忘: 序列:列表,字符串,元组len(d),d[id],del d[id],data in d函数:cmp(x,y),len(seq),list(seq)根据字符串创建列表,max( ...

  10. Python中利用函数装饰器实现备忘功能

    Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下   " ...

随机推荐

  1. 面试官:说说MySQL调优?

    MySQL 作为关系型数据库的典型代表,其流行程度超越于任何数据库,因此在 Java 面试中,MySQL 是一定会被问到的重要知识点.而在 MySQL 中有一道极其常见的面试题,我们这里系统的来看一下 ...

  2. 万字长文带你了解Java日志框架使用Java日志框架

    大家好,我是晓凡 一.日志概念 日志的重要性不用我多说了,日志,简单来说就是记录. 用来记录程序运行时发生的事情.比如,程序启动了.执行了某个操作.遇到了问题等等,这些都可以通过日志记录下来. 想象一 ...

  3. 使用BizyAir,没有显卡,也能玩AI绘图

    或许很多人跟我一样,没有显卡,但又很想玩AI绘图,但本地绘图怕是无缘了,只能借助云GPU的方式了. 今天跟大家分享一下一个简单目前可白嫖无门槛的方法实现无显卡也能玩AI绘图. 方案就是ComfyUI+ ...

  4. 一文教你如何用C代码解析一段网络数据包?【含代码】

    本文的目的是通过随机截取的一段网络数据包,然后根据协议类型来解析出这段内存. 学习本文需要掌握的基础知识: 网络协议 C语言 Linux操作 抓包工具的使用 其中抓包工具的安装和使用见下文: < ...

  5. Win32 动态库dll

    这两天学习动态库的练习,分享下方法 实例.封装窗口类的两种状态. 1.自定义窗口类QWnd 2.资源模板窗口对话框类 下面是dll的头文件,类的声明 #pragma once #ifndef _CLA ...

  6. .proto文件的作用

    在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,而在最近的开发中接触到了 Google 的 ProtoBuf. 在查阅相关资料学习 ProtoBuf 以及研读其源码之后,发 ...

  7. 安装 Google Cloud CLI(gcloud)

    安装 Ubuntu # 更新软件包索引 sudo apt update # 安装辅助工具 sudo apt install apt-transport-https ca-certificates gn ...

  8. 一个小小空格问题引起的bug

    程序员会遇到一种情况,一个bug排查到最后是由一个很小的问题导致的.在昨天的日常搬砖中遇到一个问题,耽搁了我大半天的时间,最后查明原因让我很无语. 首先介绍一下背景,我是做算法模型训练,目前手上的工作 ...

  9. Windows 将透明的图片旋转,裁剪

    使用 Microsoft Office Picture Manager 本来是想找个Java代码,跑一下 忽然在 Windows 图片打开方式中有一个  Microsoft Office Pictur ...

  10. 【YashanDB数据库】YAS-02032 column type is incompatible with referenced column type

    [标题]错误码处理 [问题分类]外键约束创建报错 [关键字]YAS-02032 [问题描述]设置外键约束报错,数据元数据不正确. [问题原因分析]外键字段类型不支持,比如varchar2(64) ,指 ...