滚雪球学 Python 第二轮封笔之文,类函数、成员函数、静态函数、抽象函数、方法伪装属性

举报
梦想橡皮擦 发表于 2021/05/26 15:51:17 2021/05/26
【摘要】 橡皮擦,一个逗趣的互联网高级网虫。新的系列,让我们一起 Be More Pythonic。 滚雪球学 Python 第二轮 已完成的文章清单 十五、类函数、成员函数、静态函数、抽象函数、方法伪装属性15.1 类函数 @classmethod15.2 静态函数 @staticmethod15.3 类函数与静态函数在继承类中的表现15.4 抽象函数 ...

橡皮擦,一个逗趣的互联网高级网虫。新的系列,让我们一起 Be More Pythonic

已完成的文章清单

  1. 滚雪球学 Python 第二轮开启,进阶之路,列表与元组那些事儿
  2. 说完列表说字典,说完字典说集合,滚雪球学 Python
  3. 关于 Python 中的字符串,我在补充两点,滚雪球学 Python
  4. 列表推导式与字典推导式,滚雪球学 Python
  5. 滚雪球学 Python 之 lambda 表达式
  6. 滚雪球学 Python 之内置函数:filter、map、reduce、zip、enumerate
  7. Python 中级知识之装饰器,滚雪球学 Python
  8. 滚雪球学 Python 之闭包操作,本系列第 8 篇文章
  9. 滚雪球学 Python 之怎么玩转时间和日期库
  10. 滚雪球学 Python 之作用域下的 global 和 nonlocal 关键字
  11. 哈希吧,滚雪球学 Python 哈希表与可哈希对象
  12. 滚雪球学 Python 之内置 random 模块
  13. Python 内置模块之 re 库,一文搞定正则表达式初阶用法,滚雪球学 Python 第 13 篇
  14. os 库、 sys 库、getopt 库 与 filecmp 库,5000 字长文带你搞定四大库

十五、类函数、成员函数、静态函数、抽象函数、方法伪装属性

本篇博客是滚雪球学 Python 第二轮的最后一篇博客,我们将内容继续提升到面向对象部分,为你补充类里面的一些装饰器,学习之后,希望你的 Python 基础知识可以更上一层楼。

15.1 类函数 @classmethod

先直接看代码,再对代码内容进行分析与学习。

class My_Class(object): # 在类定义中定义变量 cls_var = "类变量" def __init__(self): print("构造函数") self.x = "构造函数中的属于实例变量" # 类方法,第一个参数必须默认传类,一般习惯用 cls。 @classmethod def class_method(cls): print("class_method 是类方法,类名直接调用") # 类方法不可以调用类内部的对象变量(实例变量) # print(cls.x)


# 类方法可以通过类名直接调用,也可以通过对象来调用
# 即使通过实例调用类方法,Python 自动传递的也是类,而不是实例
My_Class.class_method()
my_class_dom = My_Class()
# 通过类的对象调用
my_class_dom.class_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

首先要掌握的是类函数的定义格式,在普通函数的前面添加装饰器 @classmethod,该函数就会转换为类函数,同时函数的第一个参数默认是 cls,该变量名可以任意,建议使用成 cls,这个是程序员之间的约定。

 @classmethod def class_method(cls):

  
 
  • 1
  • 2

同时类函数在调用的时候,可以通过 类名. 的形式进行调用,也可以通过 对象. 的形式调用,不过这两种调用都只是将类传递到了函数内部,不存在差异。
类函数不能调用实例变量,只能调用类变量,所谓类变量就是在类中独立声明,不在任何函数中出现的变量。在上述代码中,类变量声明部分代码如下:

class My_Class(object): # 在类定义中定义变量 cls_var = "类变量"

  
 
  • 1
  • 2
  • 3

在 Python 中,大部分 @classmethod 装饰的函数末尾都是 return cls(XXX)return XXX.__new__ () 也就是 @classmethod 的一个主要用途是作为构造函数。

15.2 静态函数 @staticmethod

先掌握一个概念,静态函数不属于它所在的那个类,它是独立于类的一个单独函数,只是寄存在一个类名下,先建立这个基本概念,后面学起来就简单很多了。

class My_Class(object): # 类变量 cls_var = "类变量" def __init__(self): # 在构造函数中创建变量 self.var = "实例变量" # 普通的对象实例函数 def instance_method(self): # 可以访问类变量 print(self.cls_var) # 可以访问实例变量 print(self.var) print("实例化方法") @staticmethod def static_method(): print(My_Class.cls_var) # 无法访问到实例变量 # print(My_Class.var) # print(self.var) print("静态方法")


my_class = My_Class()
my_class.instance_method()

# 通过对象访问
my_class.static_method()
# 类名直接访问
My_Class.static_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

即使修改成下述代码,也是错误的,静态函数的第一个参数不是实例对象 self,或者可以理解为静态函数没有隐形参数,如需要传递参数,在参数列表中声明即可。

 @staticmethod def static_method(self): print(My_Class.cls_var) # 无法访问到实例变量 # print(My_Class.var) print(self.var) print("静态方法")

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在同一个类中,调用静态方法,使用 类名.函数名() 的格式。

15.3 类函数与静态函数在继承类中的表现

先创建一个父类,其中包含两个静态函数与一个类函数。


class F(object): @staticmethod def f_static(x): print("静态方法,有 1 个参数") print(f"f_static:{x}") @staticmethod def f_static_1(): print("静态方法,无参数") return F.f_static(10) @classmethod def class_method(cls): print("父类中的类方法") return F.f_static(12)


f = F()
f.f_static(11)
f.f_static_1()

f.class_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

再编写一个 S 类继承自 F 类:

class S(F): @staticmethod def f_static(y): print("子类中重载了父类的静态方法") print(f"子类中的参数{y}") @classmethod def class_method(cls): print("子类中的类方法")


s = S()
s.f_static(110)
s.class_method()
S.class_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

测试之后,基本结论如下:
如果在子类中覆盖了父类的静态函数,那调用时使用的是子类自己的静态函数,
如果在子类中没有覆盖父类的静态函数,那调用时使用的是父类的静态函数,
类函数同样遵循该规则。

如果希望在子类中调用父类的属性或者函数,请使用 父类名. 的形式实现。

15.4 抽象函数 @abstractmethod

@abstractmethod 装饰的函数为抽象函数,含抽象函数的类不能实例化,继承了含抽象函数的子类必须覆盖所有抽象函数装饰的方法,未被装饰的可以不重写。

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化,实现代码如下:

import abc

class My_Class(abc.ABC): @abc.abstractmethod def abs_method(self): pass def show(self): print("普通")

class M(My_Class): def abs_method(self): print('xxx')

mm = M()
mm.abs_method()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

抽象基类中学习还需要了解元类相关知识,在第三轮滚雪球学 Python 中将为你展开这部分内容。

15.5 方法伪装属性

在 Python 面向对象的编码过程中,对象.属性 来获取属性的值,使用 对象.方法() 来调用方法,通过装饰器 @property 可以将一个方法伪装成属性,从而使用 对象.方法 没有括号的形式调用。代码非常简单:

class My_Class(object): def __init__(self, name): self.__name = name @property def name(self): return self.__name

m = My_Class("橡皮擦")
print(m.name)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这种写法最直接的应用,就是将部分属性变成只读属性,例如,上述代码,你无法通过下述代码对 name 进行修改。

class My_Class(object): def __init__(self, name): self.__name = name @property def name(self): return self.__name

m = My_Class("橡皮擦")
m.name = "擦哥擦姐"
print(m.name)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

如果希望方法伪装的属性具备修改和删除功能,需要参考下述代码:

class My_Class(object): def __init__(self, name): self.__name = name @property def name(self): return self.__name @name.setter def name(self, name): self.__name = name @name.deleter def name(self): del self.__name

m = My_Class("橡皮擦")
m.name = "擦哥擦姐"
print(m.name)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上述代码在将 name 方法伪装成属性之后,可以通过 @name.setter@name.deleter 对同名的 name 方法进行装饰,从而实现了修改与删除功能。

所以一般使用方法伪装属性的步骤是:

  1. @property 装饰器,可以用来将类中的方法伪装成属性;
  2. @方法名.setter 装饰器,在修改伪装成属性的方法值时会调用它;
  3. @方法名.deleter 装饰器,在删除伪装成属性的方法值时会调用它。

如果你觉得这个比较麻烦,还存在一种方法伪装属性的方式。使用 property 函数,原型如下

# 最后一个参数是字符串,调用 实例.属性.__doc__ 时的描述信息
property(fget=None, fset=None, fdel=None, doc=None)

  
 
  • 1
  • 2

通过上述函数将方法伪装成属性的代码为:

class My_Class(object): def __init__(self, name): self.__name = name def del_name(self): del self.__name def set_name(self, name): self.__name = name def get_name(self): return self.__name # 将方法伪装成属性 name = property(get_name, set_name, del_name)
m = My_Class("梦想橡皮擦")
print(m.name)
m.name = "橡皮擦"
print(m.name)

del m.name

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

15.6 这篇博客的总结

滚雪球学 Python 第二轮 15 篇博客在一次结束了,下一轮将在 4 月中旬再次开启,学 Python 我们一直在路上,希望本系列的课程对你的 Python 学习有所助力。

我是橡皮擦,滚雪球学 Python 系列课程发起者。第三轮将进入 Python Web 编程领域。

相关阅读

  1. Python 爬虫 100 例教程,超棒的爬虫教程,立即订阅吧
  2. Python 游戏世界(更新中,目标文章数 50+,现在订阅,都是老粉)
  3. Python 爬虫小课,精彩 9 讲

今天是持续写作的第 113 / 200 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞评论收藏

文章来源: dream.blog.csdn.net,作者:梦想橡皮擦,版权归原作者所有,如需转载,请联系作者。

原文链接:dream.blog.csdn.net/article/details/114962070

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。