类属性,类方法:简易的Python面向对象教程

liftword3个月前 (02-17)技术文章17

来源:麦叔编程

作者:麦叔

实例属性和类属性

在下面的例子中,Dog类的属性,比如height(身高),是属于一条具体的狗,如大黄,二黑等。他们各自有各自的height。

这种属性我们成为实例属性,实例属性通常是在init方法中通过self.xxx = yyy的形式创建的。在init中创建的实例属性的好处是,所有的实例都有这些实例属性。

也可以在后续代码中通过实例名再额外添加,比如d1.nickname = '二黑子',但这种实例属性只给当前实例d1添加了。其他实例没有,访问d2.nickname会报错。

class?Dog:
?#构造方法
?def?__init__(self,?name,?height,?power):
??self.name?=?name
??self.height?=?height
??self.power?=?power
??self.blood?=?10
??#?--省略--

d1?=?Dog('大黄',?0.7,?3)?#创建第1个实例
d2?=?Dog('二黑',?0.5,?4)?#创建第2个实例

还有一些属性,它们不属于一个特定的实例,而是所有的实例所共享的。比如狗的数量这个值,他是属于整个狗类的,而不是属于某一条狗。这种属性,我们称为类属性

添加类属性

我们现在给Dog类添加狗的数量属性(num_of_dogs)。

类属性不能通过self.xxx = yyy的形式创建,因为这样创建出来的是实例属性。

类属性的创建方式很简单:直接写在类中,不要写在init函数中,也不要加self.

#类是一个模板
class?Dog:
?num_of_dogs?=?0??#?类属性

?#构造方法?-?添加实例属性,做其他的初始化工作
?def?__init__(self,?name,?height,?power):
????????self.name?=?name
????????self.height?=?height
????????self.power?=?power
????????self.blood?=?10
????????print(f"{self.name}出生了,汪汪!")

使用类属性

类属性是属于类的,访问类属性要通过类名访问。下面的代码做了几件事情:

  1. 在init函数中,一旦创建一个新的Dog,给num_of_dogs加一.
  2. 添加了一个die()方法,表示一个Dog去世了,一旦调用了die(),num_of_dogs就会减1。
  3. 创建了多个dog,测试numer_of_dogs数量变化;循环30次,随机选择一个Dog,调用die方法。

这里用到了随机模块random,还有自加(+=),自减(-+)运算符,不熟悉请自行补充相关知识,或者加入讨论群讨论。

本案例有点血腥,请动物爱好者不要入戏太深,一切为了学习编程。可以把Dog改成其他,比如蟑螂(小强)。说到这里,我好想念我曾经的那些狗狗。

import?random

#类是一个模板
class?Dog:
????num_of_dogs?=?0??#?类属性

?#构造方法?-?添加实例属性,做其他的初始化工作
????def?__init__(self,?name,?height,?power):
????????self.name?=?name
????????self.height?=?height
????????self.power?=?power
????????self.blood?=?10
????????print(f"{self.name}出生了,汪汪!")
????????Dog.num_of_dogs?+=?1
????
????def?die(self):
????????print(f"{self.name}已安息!")
????????Dog.num_of_dogs?-=?1

#?创建100条狗,放到列表中
dogs?=?[]
for?i?in?range(100):
????d?=?Dog(f"dog{i}",?random.randint(30,?80),?random.randint(1,12))
????print(Dog.num_of_dogs)
????dogs.append(d)

#?循环30次,每次随机选择一条狗,让它死掉
for?i?in?range(30):
????dog?=?random.choice(dogs)
????dog.die()
????print(Dog.num_of_dogs)

再加1个类属性

假设我们要判定一条狗是否可以成为警犬,我们用身高height来判定,如果height超过了60就可以。这个60就是警犬的标准。这个数字是对所有的Dog是通用的,是一个类属性。

import?random

#类是一个模板
class?Dog:
????num_of_dogs?=?0??#?类属性
????police_height?=?60?#?成为警犬的身高标准

????#?--省略init和die方法

????#?判定是否可以成为警犬,返回True或者False
????def?can_be_police(self):
????????return?self.height?>?Dog.police_height

#?创建100条狗,放到列表中
dogs?=?[]
for?i?in?range(100):
????d?=?Dog(f"dog{i}",?random.randint(30,?80),?random.randint(1,12))
????print(Dog.num_of_dogs)
????dogs.append(d)

print(f'成为警犬的身高标准是:{Dog.police_height}')
for?d?in?dogs:
????if(d.can_be_police()):
????????print(f'{d.name}?可以成为警犬')

代码说明:

  • 添加了一个police_height类变量
  • 添加了一个实例方法,判定当前的dog是否可以成为警犬
  • 代码最下方打印出可以成为警犬的狗的名字

代码实践技巧

你可能会想,这个60直接写在代码里不可以吗?还要定义成变量?

直接写数字60不是不可以,但有诸多弊端:

  • 多个地方用到,可能会写错,出现不一致。
  • 如果标准从60提高到了62,要修改多个地方
  • 定义成了变量,代码更容易懂。要不然看到60个这个数字,不一定理解是什么意思。

实际上,polic_height通常不会改变,我们也可以称他为常量

常量和变量没什么区别,一般常量的名字都是全大写的,仅此而已。看到全大写就知道这个值是不会改变的,实际上是可以改变的,只是一个约定。

这里的不会改变是指不会在程序运行中动态改变。把常量的值从60改成62属于修改代码,任何时候都可以的。

类方法

仔细看一下前面定义的方法,他们都有两个特征:

  • 方法的第一个参数都是self
  • 它们都使用了实例变量,脱离了具体的实例,这些方法是无法运行的,是没有意义的

这些方法虽然都是共同的,但是他们的运行过程依赖了实例变量,所以他们都是实例方法。类中的方法默认就是实例方法。

import?random

#类是一个模板
class?Dog:
????num_of_dogs?=?0??#?类属性
????police_height?=?60?#?成为警犬的身高标准

?#构造方法?-?添加实例属性,做其他的初始化工作
????def?__init__(self,?name,?height,?power):
????????self.name?=?name
????????self.height?=?height
????????self.power?=?power
????????self.blood?=?10
????????print(f"{self.name}出生了,汪汪!")
????????Dog.num_of_dogs?+=?1
????
????def?die(self):
????????print(f"{self.name}已安息!")
????????Dog.num_of_dogs?-=?1

????#?判定是否可以成为警犬,返回True或者False
????def?can_be_polic(self):
????????return?self.height?>?Dog.police_height

这3个方法都是实例属性有关,都是实例方法。

但有的方法和具体的实例无关,而是和整个狗类有关。比如有方法狗类宣言,它的功能是:

  • 打印狗类宣言
  • 介绍狗类的数量

看代码:

import?random

#类是一个模板
class?Dog:
????num_of_dogs?=?0??#?类属性
????police_height?=?60?#?成为警犬的身高标准

????#--省略--
????
????#?类方法
????@classmethod?
????def?wangwang(cls):
????????print('我们是狗,我们是人类的朋友')
????????print('''
^..^??????/
/_/\_____/
/\???/\
/??\?/??\
????????''')
????????print(f'我们共有{cls.num_of_dogs}个成员')

#?--省略--????

Dog.wangwang()

代码说明:

  • 添加的类方法wangwang()
  • 类方法的前面要添加:@classmethod。这是一个装饰器。不懂装饰器?请看本文最后的文章列表。
  • 类方法的第一个参数是cls,是class的缩写,表示当前类。使用cls可以访问类属性或者其他类方法。
  • 调用类方法使用类名:Dog.wangwang()

静态方法

我们可以看到类方法对类属性有所依赖,有些方法对实例属性和类属性都没有依赖,也不需要传入self或者cls,这些方法就是静态方法。

假设我们有另外几个方法:只是打印狗类的字符画,不用打印狗的数量或者其他。没有任何类属性或者实例属性的依赖。

看代码:

import?random

#类是一个模板
class?Dog:
????num_of_dogs?=?0??#?类属性
????police_height?=?60?#?成为警犬的身高标准

????#?--省略--
????
????#?类方法
????@classmethod?
????def?wangwang(cls):
????????print('我们是狗,我们是人类的朋友')
????????print('''
^..^??????/
/_/\_____/
/\???/\
/??\?/??\
????????''')
????????print(f'我们共有{cls.num_of_dogs}个成员')

????#静态方法:小狗的图像
????@staticmethod
????def?pic_little():
????????print('''
??/^?^\?
?/?0?0?\?
?V\?Y?/V
??/?-?\?
?/????|
V__)?||
????????''')

????#静态方法:大狗的图像
????@staticmethod
????def?pic_big():
????????print('''
????___
?__/_??`.??.-"""-.
?\_,`?|?\-'??/???)`-')
??"")?`"`????\??((`"`
?___Y??,????.'7?/|
(_,___/...-`?(_/_/?
????????''')
????
????#静态方法:长的图像
????@staticmethod
????def?pic_long():
????????print('''
????????????????????????????????????.-.
?????(___________________________()?`-,
?????(???______________________???/''"`
?????//\\??????????????????????//\\
?????""?""?????????????????????""?""

????????''')


#--省略--

Dog.wangwang()
Dog.pic_little()
Dog.pic_big()
Dog.pic_long()

代码说明:

  • 添加了3个静态方法,分别打印3种不同的狗的图像
  • 静态方法前面必须加:@staticmethod,这是一个装饰器。不懂装饰器,看本文最后的文章列表。
  • 静态方法不强制要求传入self或者cls。
  • 调用静态方法通过类名。

练习:

今天的练习:

1. 给Dog类添加一个类属性dog_list,它是一个列表,用来保存所有创建出来的类。
2. 修改init函数,把每个新建的Dog添加到dog_list中。
3. 修改die函数,从dog_list中移除去世的Dog。
4. 删掉num_of_dogs属性,添加一个类方法num_of_dogs,返回狗的数量。提示:通过dog_list的长度确定狗的数量。
5. 修改所有值钱用到num_of_dogs的地方,改成使用新定义的方法,而不是变量名。

相关文章

python极简教程:对象的方法_python对象的含义

这一场,主讲python的面向对象的第二部分—— 对象的方法 。目的:掌握Python对象的五个核心方法。init和new讲解之前,先上一段代码class Demo: def __init__...

Python类和对象详解_python中的类和对象,属性和方法

Python中的类(Class)和对象(Object)是面向对象编程的核心概念。通过类来定义对象的属性和行为,而对象则是类的实例。1. 类(Class)类是用来创建对象的模板,它定义了对象的属性(数据...

如何用 Python 构建一个决策树_python决策结构

本文最初发布于 Medium 网站,经原作者授权由 InfoQ 中文站翻译并分享。决策树是一个经久不衰的话题。本文要做的是将一系列决策树组合成一个单一的预测模型;也就是说,我们将创建集成方法(Ense...

详细介绍一下Python中的类与对象_python对象和类的关系

类和对象是面向对象编程的两个核心概念,而Python作为一门支持面向对象的编程语言,也是需要通过类和对象来实现代码的组织和封装的,下面我们就来详细介绍一下Python中的类与对象,来帮助大家一起了解它...

Python基础之对象、面向对象编程_python面向对象程序

本系列内容所用Python版本为anaconda,直接浏览器搜索下载安装即可!前面内容我有多次提到过对象这个概念,今天我想专门出一期内容,浅谈一下Python的对象这一基础概念,并谈谈我对面向对象编程...

python大师讲解python对象类型_python 对象类型

Python对象的基本概念对象是Python中最基本概念。Python程序可以分解成模块、语句、表达式、以及对象程序由模块构成模块包含语句语句包含表达式表达式建立并处理对象Python内置类型Pyth...