1. 概览

先定义一个最简单的 Python 3 的类:

class MyClass:def method(self):print('我是实例方法', self)@classmethoddef classmethod(cls):print('我是类方法', cls)@staticmethoddef staticmethod():print('我是静态方法')

1.1 实例方法

第一个方法 method(self) 方法是 实例方法 instance method 。 当 method 被调用时, self 参数指向 MyClass 类的一个实例。 实例方法可以通过 self 自由地访问同一对象的属性和其它方法,这样它们可以修改实例的状态。 注意实例方法可以通过 self.__class__ 属性来获取到类,所以实例方法也可以更改类的状态。

1.2 类方法

第二个方法 classmethod(cls) 是 类方法 class method 。 上面需要写一个 @classmethod 装饰器。 类方法接收一个 cls 参数,当该方法被调用的时候,它指向类(而不是类的实例)。 类方法只有 cls 参数,所以它 不能 修改实例的状态。 修改实例的状态必须要有 self 参数。 类方法只能修改类的状态,类状态的更改会作用于所有该类的实例。

1.3 静态方法

第三个方法 staticmethod() 是 静态方法 static method 。 它上面要有一个 @staticmethod 装饰器。 静态方法不能修改类或者实例的状态,它受限于它所接收的参数。 我们一般用这种方法来隔离命名空间。

2. 实际应用

2.1 调用实例方法

首先创建一个实例,然后调用一下实例方法:

obj = MyClass()# 调用实例方法
obj.method()
"""
我是实例方法 <__main__.MyClass object at 0x00000213E209B898>
"""

还可以这样调用:

MyClass.method(obj)
"""
我是实例方法 <__main__.MyClass object at 0x00000213E209B898>
"""

使用 对象.实例方法() 这种点号调用的形式是一个语法糖, Python 会自动把 对象 作为第一个实参,传递给 实例方法 中的 self 形参。 如果使用 类.实例方法(对象) 这种形式,则必须手动传递 对象 给 实例方法 的第一个参数 self 。

如果不创建实例就调用实例方法,或者是不传入 对象 ,那么就会出错:

# 不创建实例就调用实例方法会发生什么?
# 会提示缺少位置参数 self
# 实例方法依赖于实例而存在
MyClass.method()
"""
Traceback (most recent call last):File "test.py", line 28, in <module>MyClass.method()
TypeError: method() missing 1 required positional argument: 'self'
"""

实例方法可以通过 self.__class__访问到类。

# 打印类名
print(obj.__class__.__name__)
"""
MyClass
"""

2.2 调用类方法

下面来调用一下类方法。

# 通过类名调用类方法
MyClass.classmethod()
# 也会自动传递类名作为第一个参数
"""
我是类方法 <class '__main__.MyClass'>
"""

通过 类.类方法() 的形式调用类方法, Python 会自动把 类 作为第一个参数传递给 类方法 的第一个参数 cls ,我们不用手动传递。

也可以用实例调用类方法:

# 当然也可以通过实例调用类方法
obj.classmethod()
"""
我是类方法 <class '__main__.MyClass'>
"""

通过实例调用类方法, Python 会把该实例的类传递给 类方法 的 cls 参数,该实例的类未必是定义类方法的类。如下例:

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 父类
class Animal:@classmethoddef classmethod(cls):print('cls是:' + str(cls.__name__))# 子类
class Dog(Animal):passdog = Dog()
dog.classmethod()
"""
cls是:Dog
"""
# 注意不是类方法的定义类:Animal
# 而是实例的所属类:Dog

2.3 调用静态方法

最后调用一下静态方法:

# 调用静态方法
obj.staticmethod()
"""
我是静态方法
"""
# 调用静态方法的时候
# 点号语法不会自动传递任何参数

通过 实例.静态方法() 调用静态方法的时候, Python 不会传递 self 和 cls ,以此来限制静态方法的权限。所以静态方法不能获取实例或者类的状态。 它们就像普通函数一样,只不过隶属于类和该类的每个实例的命名空间。

2.4 不创建实例调用方法

不创建实例,调用实例方法、类方法和静态方法。

# 不创建实例,调用类方法
MyClass.classmethod()
"""
我是类方法 <class '__main__.MyClass'>
"""# 不创建实例,调用静态方法
MyClass.staticmethod()
"""
我是静态方法
"""# 不创建实例,调用实例方法
MyClass.method()
"""
Traceback (most recent call last):File "test.py", line 85, in <module>MyClass.method()
TypeError: method() missing 1 required positional argument: 'self'
"""

不创建实例,调用实例方法出错。 这是可以理解的,因为我们直接通过类这个蓝图 blueprint 本身来调用实例方法, Python 无法给 self 传参。

3. 使用类方法实现披萨工厂

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Pizza:def __init__(self, ingredients):self.ingredients = ingredientsdef __repr__(self):return f'披萨({self.ingredients})'@classmethoddef margherita(cls):return cls(['马苏里拉奶酪', '番茄'])@classmethoddef prosciutto(cls):return cls(['马苏里拉奶酪', '番茄', '火腿'])

使用类方法作为工厂函数,生产不同种类的披萨。

【注】 工厂函数 factory function 工厂函数是一个函数,它根据不同的输入,新建并返回不同的对象。

注意在工厂函数中,没有直接使用 Pizza 这个类名,而是使用了 cls 这个参数。 这样的好处在于易于维护。 万一以后要把 Pizza 这个类名改成 披萨 ,只改动一处就行,因为类方法中用的是 cls 而不是直接写 类名 。 这是遵循 DRY 原则的一个小技巧( Don’t repeat yourself )

现在使用工厂函数来生成几个披萨吧:

pizza1 = Pizza.margherita()
print(pizza1)
"""
披萨(['马苏里拉奶酪', '番茄'])
"""pizza2 = Pizza.prosciutto()
print(pizza2)
"""
披萨(['马苏里拉奶酪', '番茄', '火腿'])
"""

我们可以使用工厂函数来创建事先配置好的 Pizza 对象。 这些工厂函数内部都使用了 init 构造函数,它们提供了一个捷径,不用记忆各种披萨配方。 从另外一个角度来看,这些 类方法可以为一个类定义多个构造函数 。

4. 何时使用静态方法

改写上面写的 Pizza 类。

'''
学习中遇到问题没人解答?小编创建了一个Python学习交流QQ群:531509025
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import mathclass Pizza:def __init__(self, radius, ingredients):self.radius = radiusself.ingredients = ingredientsdef __repr__(self):return (f'披萨({self.radius!r}),'f'{self.ingredients!r})')def area(self):return self.circle_area(self.radius)@staticmethoddef circle_area(r):return r ** 2 * math.pi

试一试使用静态方法:

# 生成披萨
p = Pizza(4, ['马苏里拉奶酪', '番茄'])
print(p)
"""
披萨(4,['马苏里拉奶酪', '番茄'])
"""# 计算披萨的面积
p.area()
print(p.area())
"""
50.26548245743669
"""# 通过类调用静态方法
print(Pizza.circle_area(4))
"""
50.26548245743669
"""# 通过对象调用静态方法
print(p.circle_area(4))
"""
50.26548245743669
"""

把一个方法写成静态方法的好处:

  • 表明它不会更改类或者实例的状态
  • 更容易写测试代码,不用进行实例化就可以测试静态方法

5. 总结

调用实例方法,需要一个实例。实例方法可以通过 self 来获取实例。

self
self

类方法可以用实例或者类来调用。类方法可以通过 cls 获取类本身。类方法上面要加 @classmethod 装饰器。

  • 通过实例调用类方法,不用手动传类到 cls 。 通过实例调用的类方法, Python 自动传递到 cls 的类是该对象的所属类,不一定是定义该类方法的类。(比如父类定义了类方法,子类继承父类。通过子类的实例调用父类的类方法,传到 cls 中的参数是子类,而不是定义类方法的父类。)
  • 通过类调用类方法,也不用手动传类到 cls 。

静态方法可以用实例或者类调用。

静态方法无法获取到 cls 和 self 。 静态方法上面要加 @staticmethod 装饰器。

类方法和静态方法,从某种程度上传达了类的设计意图,使代码易于维护。

针对Python 实例方法、类方法和静态方法的详解相关推荐

  1. Python 实例方法,类方法和静态方法的区别

    在 Python 中,实例方法(instance method),类方法(class method)与静态方法(static method)经常容易混淆.本文通过代码例子来说明它们的区别. 1.实例方 ...

  2. python 类可以调用实例变量_Python实例方法、类方法、静态方法区别详解

    1.关于参数的区别 实例方法:定义实例方法是最少有一个形参 ---> 实例对象,通常用 self 类方法:定义类方法的时候最少有一个形参 ---> 类对象,通常用 cls 静态方法:定义静 ...

  3. python 类方法 实例方法的区别_python基础教程Python实例方法、类方法、静态方法区别详解...

    1.关于参数的区别 实例方法:定义实例方法是最少有一个形参 ---> 实例对象,通常用 self 类方法:定义类方法的时候最少有一个形参 ---> 类对象,通常用 cls 静态方法:定义静 ...

  4. python静态变量和静态方法_详解Python中的静态方法与类成员方法

    前言 因为Python的水平目前一直是处于能用阶段,平时写的脚本使用的Python的写法也比较的简单,没有写过稍微大一点的项目.对Python中的类,类之间的组织关系,整个项目中类之间如何耦合还缺乏认 ...

  5. 21年最新Python面试题及答案汇总详解(上)

    错过三月找工作的机会,还要错过四月的好时期吗?Python面试你做准备了吗?下面小编整理了一套2021年最新Python常见面试题目,及Python面试题目答案汇总.希望能够帮助到大家. 21年最新P ...

  6. python平方数迭代器_对python中的高效迭代器函数详解

    python中内置的库中有个itertools,可以满足我们在编程中绝大多数需要迭代的场合,当然也可以自己造轮子,但是有现成的好用的轮子不妨也学习一下,看哪个用的顺手~ 首先还是要先import一下: ...

  7. python3占位符详解_占位符最新:Python 占位符的使用方法详解_爱安网 LoveAn.com

    关于"占位符"的最新内容 聚合阅读 这篇文章主要介绍了Python 占位符的使用方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以 ...

  8. python popen阻塞_对Python subprocess.Popen子进程管道阻塞详解

    问题产生描述 使用子进程处理一个大的日志文件,并对文件进行分析查询,需要等待子进程执行的输出结果,进行下一步处理. 出问题的代码 # 启用子进程执行外部shell命令 def __subprocess ...

  9. python画图三维-对python mayavi三维绘图的实现详解

    网上下载mayavi的官方帮助文档,里面有很多例子,下面的记录都是查看手册后得到的. python的mayavi.mlab库中的绘图函数有很多候选参数,但下文记录并没有过多讨论,本人也是需要用到才查看 ...

最新文章

  1. Vue状态管理之Vuex
  2. Unified Networking Lab 安装使用IOL镜像
  3. java8获取实现某个接口的所有类_Java 试题八
  4. vim ctrlp_使用Ctrlp和Ctag使Vim更智能
  5. C语言(CED)对于一个2行N列的走道。现在用1*2,2*2的砖去铺满。问有多少种不同的方式(递归求解)
  6. Tinker 热修复框架模拟使用
  7. 机器学习与深度学习神器!凸优化(Convex Optimization)学习必备
  8. Linux Gedit 报出警告
  9. Ubuntu 安装 中文输入法(Google 拼音)
  10. Hibernate的单向N-1关联(一)
  11. Android 滑动界面实现---Scroller类别 从源代码和开发文档了解(让你的移动布局)...
  12. python编程基础及应用教程答案_Python编程基础教程
  13. python灰色预测模型_GM(1,n)(灰色模型代码)
  14. 常见无线电重点频段划分及参数设置
  15. vue-cli中配置gzip压缩
  16. 基于JAVA HAPI包以树形结构实现可配置式 HL7消息接收与解析
  17. asp.net模糊查询
  18. Python试题和规范文档
  19. windows批处理(cmd/bat脚本)编程详解
  20. 图解转盘抽奖布局样式,以小程序为例

热门文章

  1. cinder配置多ceph储存池[Ceph and Cinder multi-backend]
  2. 提高收益 酒店大数据之客户数据收集
  3. 使用vue.js devtools遇到的磕磕绊绊
  4. JAVA总结之数组篇
  5. Docker容器学习梳理--日常操作总结
  6. Dreamweaver操作常见的问题
  7. IE7一样可以轻易重装
  8. android 恶意广告,CheckPoint:Android恶意广告软件SimBad被下载近1.5亿次
  9. 回调函数 相当于线程_Java中的回调机制,这篇给你整的明明白白的
  10. 【目录】Python 入门基础篇 <(^-^)>