本文学习自知乎(木头人):https://zhuanlan.zhihu.com/p/30024792
类是模板,而实例则是根据类创建的对象。
以圆为例,圆是具有圆周率(pi)和半径®两个相似特征的属性。根据相似特征抽象出圆类,每个圆的半径可以不同,那么半径可以作为圆的实例属性;而每个圆的圆周率pi是相同的,那么圆周率pi就可以作为类属性,这样就定义出了一个圆类。而我们要知道圆的面积,周长等可以通过类方法计算出来。
1.Python类的定义与实例的创建
在Python中,类通过 class 关键字定义,类名通用习惯为首字母大写,Python3中类基本都会继承于object类,语法格式如下,我们创建一个Circle圆类:
class Circle(object): # 创建Circle类,Circle为类名
pass # 此处可添加属性和方法
如下我们创建两个Circle类的实例:
circle1= Circle()
circle2= Circle()
2.Python类中的实例属性与类属性
类的属性有两种,一种是类属性,一种是实例属性。
类属性是每个实例共有的属性,实例属性用于区分不同的实例。
2.1 实例属性
在定义 Circle 类时,可以为 Circle 类添加一个特殊的__init__()
方法,当创建实例时,__init__()
方法被自动调用为创建的实例增加实例属性。
class Circle(object): # 创建Circle类
def __init__(self, r): # 初始化一个属性r(不要忘记self参数,他是类下面所有方法必须的参数)
self.r = r # 表示给我们将要创建的实例赋予属性r赋值
注意:__init__()
方法的第一个参数必须是 self(self代表类的实例,可以用别的名字,但建议使用约定成俗的self),后续参数则可以自由指定,和定义函数没有任何区别。
相应,创建实例时就必须要提供除 self 以外的参数:
circle1 = Circle(5) # 创建实例时直接给定实例属性,self不算在内
circle2 = Circle(10)
print(circle1.r) # 实例名.属性名 访问属性
print(circle2.r) # 我们调用实例属性的名称就统一了
注意:实例名.属性名 circle1.r 访问属性,是我们上面Circle类__init__()
方法中 self.r 的 r 这个实例属性名,而不是__init__(self, r)
方法中的 r 参数名,如下更加容易理解:
class Circle(object): # 创建Circle类
def __init__(self, R): # 约定成俗这里应该使用r,它与self.r中的r同名
self.r = R
circle1 = Circle(1)
print(circle1.r) #我们访问的是小写r
面试喜欢问的问题:创建类时,类方法中的self是什么?
self 代表类的实例,是通过类创建的实例 (注意,在定义类时这个实例我们还没有创建,它表示的我们使用类时创建的那个实例)
2.2 类属性
绑定在实例上的属性不会影响其他实例,但类本身也是一个对象,如果在类上绑定属性,则所有实例都可以访问该类的属性,并且所有实例访问的类属性都是同一个!!!记住,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。
圆周率π为圆的共有属性,我们可以在Circle类添加pi这个类属性,如下:
class Circle(object):
pi = 3.14 # 类属性
def __init__(self, r):
self.r = r
circle1 = Circle(1)
circle2 = Circle(2)
print('----未修改前-----')
print('pi=\t', Circle.pi)
print('circle1.pi=\t', circle1.pi) # 3.14
print('circle2.pi=\t', circle2.pi) # 3.14
print('----通过类名修改后-----')
Circle.pi = 3.14159 # 通过类名修改类属性,所有实例的类属性被改变
print('pi=\t', Circle.pi) # 3.14159
print('circle1.pi=\t', circle1.pi) # 3.14159
print('circle2.pi=\t', circle2.pi) # 3.14159
print('----通过circle1实例名修改后-----')
circle1.pi=3.14111 # 实际上这里是给circle1创建了一个与类属性同名的实例属性
print('pi=\t', Circle.pi) # 3.14159
print('circle1.pi=\t', circle1.pi) # 实例属性的访问优先级比类属性高,所以是3.14111
print('circle2.pi=\t', circle2.pi) # 3.14159
仔细观察我们通过类创建的实例修改的类属性后,通过其他实例访问类属性他的值还是没有改变。其实是通过实例修改类属性是给实例创建了一个与类属性同名的实例属性而已,实例属性访问优先级比类属性高,所以我们访问时优先访问实例属性,它将屏蔽掉对类属性的访问。
我们删除circle1实例的实例属性pi,就能访问该类的类属性了。
print('----删除circle1实例属性pi-----')
del circle1.pi
print('pi=\t', Circle.pi)
print('circle1.pi=\t', circle1.pi)
print('circle2.pi=\t', circle2.pi)
可见,千万不要在实例上修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个实例属性。
3.Python类的实例方法
方法是表明这个类用是来做什么。
在类的内部,使用 def 关键字来定义方法,与一般函数定义不同,类方法必须第一个参数为 self, self 代表的是类的实例(即你还未创建类的实例),其他参数和普通函数是完全一样。
如下我们给圆类 Circle 添加求面积的方法 get_area :
class Circle(object):
pi = 3.14 # 类属性
def __init__(self, r):
self.r = r # 实例属性
def get_area(self):
""" 圆的面积 """
# return self.r**2 * Circle.pi # 通过实例修改pi的值对面积无影响,这个pi为类属性的值
return self.r**2 * self.pi # 通过实例修改pi的值对面积我们圆的面积就会改变
circle1 = Circle(1)
print(circle1.get_area()) # 调用方法 self不需要传入参数,不要忘记方法后的括号 输出 3.14
注意:示例中的 get_area(self) 就是一个方法,它的第一个参数是 self 。__init__(self, name)
其实也可看做是一个特殊的实例方法。
在方法的内部需要调用实例属性采用 "self.属性名 " 调用。示例中 get_area(self)
对于 pi 属性的引用 Circle.pi
与 self.pi
存在一定区别。
Circle.pi 使用的是类属性 pi,我们通过创建的实例去修改 pi 的值对它无影响。self.pi 为实例的 pi 值,我们通过创建的实例去修改 pi 的值时,由于使用 self.pi 调用的是实例属性,所以 self.pi 是修改后的值。
调用实例的方法中使用实例属性可采用 实例名.方法名(除self的参数) 使用。