面向对象编程是最有效的软件编写方法之一,而Python在设计之初,就已经被设定成支持面向对象编程的编程语言;
一.尝试编写一个类
在学习之前,先了解一些它的基本特征:
- 类 :类是用来描述具有相同属性和方法的对象的集合,类是一种抽象概念,基于对象之上的抽象;
- 方法:类中定义的函数;
- 属性:类中的变量称为属性;
- 对象:对象是一个实体,它是类定义的数据结构实例;
<注意> 这里一定要理解类和对象的本质,类是对一类对象的抽象,它是抽象概念,而对象是看得见摸得着的实体;
先介绍这么多就OK,那么接下来先写一个类练练手:Dog类
我们设定dog都具有姓名和名字,并赋予它蹲下(sit())和打滚(roll())的能力:
class Dog():
'''这是一个Dog类,这种Dog不会咬人'''
def __init__(self,name,age):
self.name = name
self.age = age
def sit(self):
print(self.name.title() + " is now sitting.")
def roll(self):
print(self.name.title() + "rolled over!")
在Python中,类的命名一般以大写字母开头,这点要注意;
对Dog类的说明:
1.init()方法
前文已经提过,类中定义的函数称为方法,在本例中用到了一个特殊的方法,即__init__()方法,也叫做构造方法,每当你利用Dog()类创建实例时,Python都会自动运行这个方法;
构造方法用于构造该类的对象并返回,它是一个类创建对象的根本途径,举个例子说明:如果现在我要用我刚创建的Dog类构造一个对象,那么有:
my_dog = Dog('Tom',7)
这里就创建了一个实例:my_dog,my_dog的name是Tom,age是7,在调用Dog类时,Python已经自动运行了__init__()方法创建了一个实例,并让my_dog指向它,利用my_dog可以调用Dog类中的所有变量和方法;
应该注意到Dog类中的__init__()方法有三个参数,第一个self参数先忽略(用法稍后会提到),后面两个参数刚好对应创建实例时调用Dog类传入的参数;
init()方法是构造对象必须的,如果一个类中你没有指定__init__()方法,那么Python将自动创建一个__init__()方法,它的参数是self;
2.self参数
self代表类的实例,即对象本身,self代表了当前对象的地址;
Python中约定俗成的类中代表对象本身的参数为self,但这并不是官方硬性规定,当然你也可以用别的名称替代;
self参数是类中的所有方法都必须要具备的,即使你定义的方法内容与对象毫不相干,你还是要在该方法中添加self参数;例:
class Dog():
'''这是一个Dog类,这种Dog不会咬人'''
def __init__(self,name,age):
self.name = name
self.age = age
def sit(self):
print(self.name.title() + " is now sitting.")
def roll(self):
print(self.name.title() + "rolled over!")
def text():
print('hahaha')
f = Dog('Tom',6)
f.text()
运行这段代码,在测试方法text中没有添加任何参数时,运行出现错误:
TypeError: text() takes 0 positional arguments but 1 was given
添加self参数后,成功运行,输出hahaha;
另外注意,即使是把对象中的方法赋值给函数,该函数也会自动绑定方法中的第一个参数,即self,例:
text = f.sit
text() #Tom is now sitting.
可以看到text()函数不需要传入参数;
3.类对象的使用
创建一个类后,就可以用它创建对象了,你可以根据需要创建n个对象,它们都可以调用类中的方法或者访问类中的属性,这里不再举例;
值得一提的是,Python支持动态的给对象添加属性或者方法:
<添加属性>
添加属性很简单,直接为对象或者类赋值即可,这里注意区分给对象添加属性与给类添加属性的差别,例:
class Stu():
grade = 'first grade'
def __init__(self,name,age):
self.name = name
self.age = age
def gra(self):
print(self.name.title() + "'s grade is " + Stu.grade + ".")
这个类非常简单,唯一需要注意的一点在于gra()函数中不能直接 + grade ,如果这样Python会报错,理由是grade尚未被定义,但是在类的开头明明已经定义了一个grade并赋值了啊,从这儿就说明了类中的属性在使用时不使用前缀加属性的方式是无意义的,即使是在类的内部也必须这样用;因此这里把grade换成Stu.grade;
之后创建一个实例:
stu1 = Stu('Tom',15)
print(stu1.grade)
#运行结果:
first grade
添加一个属性:
stu1.result = 100 #添加属性成绩,默认为100
print(stu1.result) #100
这里注意result添加在stu1对象内,原类中并没有被改变,因此如果再创建一个实例的话,新创建的实例里面仍然是没有result属性的,如果想在类中添加,把前缀换为类名即可,这样你以后创建的每个实例都会有result属性;
你也可以删除属性,删除用del语句,但是要记住类中的属性以及以类名为前缀所添加的属性不能用以对象为前缀的形式删除;
<添加方法>
介绍两种 添加方法 的方法:
a.先看例子
def info(self):
print(self.name.title() + "'s age is " + str(self.age) + ".")
stu1.info = info
stu1.info(stu1) #Tom's age is 15.
分析:先定义一个函数,然后和添加属性时差不多直接令 stu1.info = info ,这时就已经在对象stu1中添加了info方法,但是这种添加方式有一个不便的地方就是Python不会自动将对象本身绑定到它们的第一个参数上面,在本例中就是Python不能自动将stu1对象传入self中去,因此每次调用info方法都需要手动传入;
<注意>需要手动传入self参数是针对于在对象中添加方法的情况,如果你直接在类中添加了方法,那么实例化的对象中该方法依然能够自动绑定,就不需要手动传入self参数了;
这里值得一提的是,如果不是针对于添加方法,而只是调用方法的话,用对象调用方法会自动绑定self,而用类名调用不会自动绑定;
b.利用MethodType自动绑定
from types import MethodType as mt
def info(self):
print(self.name.title() + "'s age is " + str(self.age) + ".")
stu1.info = mt(info,stu1)
stu1.info() #Tom's age is 15.
【flag】MethodType的详细用法了解后补上;
二.类属性与实例属性
在类中,根据属性定义的位置不同,可以把类分为类属性与实例属性,其中类属性是指在类中定义,独立于类中方法之外的属性,而实例属性则是类中方法之内的属性;
两者的区别:
- 类属性既可以用类名访问,也可以用对象名访问,但推荐用类名访问,原因在第三条;
- 实例属性只能由对象名访问,不能用类名访问;
- 类对象访问类中的属性时,会优先访问实例属性,而实例属性与类属性允许重名,因此要格外注意;
例:
class Stu():
result = 80
def info(self):
self.result = 100
在这个类中,有两个result,我们来访问它
首先创建一个实例化对象:
stu1 = Stu()
访问:
print(Stu.result) #80
print(stu1.result) #80
似乎和预想不一样?
继续看:
stu1 = Stu()
stu1.info()
print(Stu.result) #80
print(stu1.result) #100
知道问题所在了么?在第一个例子中,info方法并没有运行,因此self.result尚未被定义,于是用对象访问实例属性时找不到result,就继续访问类属性的result了,因此两个都是80;
三.类方法、静态方法、实例方法
<实例方法>
一个类中用的最多的就是实例方法,实例方法要求参数必须有一个self,事实上在以上编写的所有类中,它们都是实例方法;
<类方法>
与实例方法不同的是,类方法Python会自动绑定类本身,实例方法是自动绑定对象本身,因此类方法中也有一个和self类似的参数,一般写作cls,类方法用的不多,定义类方法的方式:
class Dog():
@classmethod #类方法需要用 @classmethod修饰
def sit(cls):
print('The dog is sitting.')
dog = Dog()
Dog.sit() #The dog is sitting.
dog.sit() #The dog is sitting.
<静态方法>
静态方法就是函数,静态方法用@staticmethod修饰,静态方法不会自动绑定参数,完全就和函数一样,它与类的关系也不大,一般用的比较少;
部分内容(不限于本文)参考自: