还记得第一次接触Python是在刚刚进入大学的第一个学期,之后就没有再使用过python。虽然基本语法,内置函数等没有忘记,但最近的学习中,一直在用python、matlab混合编程,零零散散的知识点、模模糊糊的记忆,有时候令我眼花缭乱。现在趁着闲暇,系统的温习一下python的基础知识。
目录
一、初识对象
二、self参数详解
三、变量的划分(类变量、实例变量、局部变量)
四、实例方法、静态方法、类方法
一、初识对象
我依稀记得,大一上python的某堂课,任课老师让我们用一段python程序描述班级中学生的信息。
A同学回答:我的程序是这样的
student_1={
"姓名":sun,
"性别":男,
"籍贯":河南,
年龄:18
}
A同学选择使用字典来描述信息。
B同学回答:我的程序是这样的
student_2=["王五","南京","男",19]
他使用列表描述学生信息。
C同学回答道:我选择使用字符串描述信息
student_3="张柳来自福建省,他是男生,今年19岁"
不难看出,由于不同的同学采用不同数据类型的变量来描述学生信息,使得数据很乱。我们有没有什么其他办法来统一数据格式,使得学生信息看起来整齐有序呢?
我们发现要描述一个学生的信息,形式是多样的(比如使用列表,字典等),但是无论是什么形式都需要包含姓名,年龄,性别,籍贯几个要素。不同的学生的姓名、年龄等个不相同,但是要描述清楚这个学生的信息,总得有这几个要素。如果我们使用一个“模板”,这个“模板”中包含姓名、性别、年龄、籍贯这几个要素,那么这个“模板”肯定能描述清楚任何一个特定学生的信息。因为任何一个特定的学生信息都是从这个“模板”中得来的,数据格式就变得整齐有序了。
上面这个过程其实就是使用对象来组织数据,其形式如下:
class Student:
name:None
age:None
sex:None
birthPalce:None
那么A、B、C三位同学的程序就可以统一为:
stu_1=Student()
stu_1.name="sun"
stu_1.age=18
stu_1.sex="男"
stu_1.birthPalce="河南"
stu_2=Student();
stu_2.name="王五"
stu_2.age=19
stu_2.sex="男"
stu_2.birthPalce="南京"
stu_3=Student();
stu_3.name="张柳"
stu_3.age=19
stu_3.sex="男"
stu_3.birthPalce="福建"
在上面的几段程序中
class Student:
name:None
age:None
sex:None
birthPalce:None
称为设计类。其中name,age,sex等称为类的属性,它是用来描述类的特征。比如,一个学生年龄19岁,籍贯河南,姓名李华,这里的年龄、籍贯、姓名就是这个学生的特征(或者称为属性),而19岁、河南,李华就是对应属性的值。又比如,丽丽长的很漂亮,这里丽丽的属性就是“外貌’,而对应的属性值是“漂亮”。其实,要全面描述清楚一个学生,单凭使用“年龄”“姓名”“外貌”等属性是描述不清的,一个学生还可以学习、社交、考试等等,我们无法使用属性来解释学生的这些行为。这时候就要使用“方法”来描述了,现在我们来拓展一下上面的学生类,在类中添加学生的“行为”:
class Student:
name:None
age:None
sex:None
birthPalce:Nonedef study(self):
print(f"大家好,我是{self.name},我非常擅长数学,如果有数学难题,欢迎一起探讨")
我们在类中定义了一个函数"study"来描述学生的学习行为,这里在类中定义的函数就称为类的成员方法,它是用来描述类的功能。在方法定义的参数列表中,有一个self关键字,它是用来表示类对象自身的意思当我们使用类对象调用方法的是,self会自动被python传入。关于self参数,在本篇文章的下一个章节会详细介绍。
在上面的程序段中:
stu_1=Student()
stu_2=Student();
stu_3=Student();
称为创建对象。为什么非要创建对象才能使用呢?类是程序中的“设计图纸”,而对象是基于图纸生产的具体实体,只要创建出对象,才能用实体去实现类的功能。
类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作这种套路,称之为:面向对象编程。面向对象编程就是根据实体抽象出共性,设计出类,基于类创建对象,由对象做具体的工作。
我们继续看上面的程序段:
stu_1.name="sun"
stu_1.age=18
stu_1.sex="男"
stu_1.birthPalce="河南"
stu_2.name="王五"
stu_2.age=19
stu_2.sex="男"
stu_2.birthPalce="南京"
stu_3.name="张柳"
stu_3.age=19
stu_3.sex="男"
stu_3.birthPalce="福建"
称为对象属性赋值。但是在这段对象属性赋值代码中,为对象的属性赋值需要依次进行,略显繁琐。有没有更加高效的方式就完成呢?
答案是肯定的,我们完全可以使用构造方法对成员变量进行赋值。
class Student:
def __init__(self,name,age,sex,birthPlace):
self.name=name
self.age=age
self.sex=sex
self.birthPlace=birthPlace
Python类可以使用:__init__()方法,称之为构造方法。
它可以实现:
在创建类对象(构造类)的时候,会自动执行。
在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用。借此特性可以给成员变量赋值
值得注意的是:构造方法名称:__init__ 千万不要忘记init前后都有2个下划线,构造方法也是成员方法,不要忘记在参数列表中提供:self
二、self参数详解
self 参数的具体作用是什么呢?举一个例子,如果把类比作造房子的图纸,那么类实例化后的对象是真正可以住的房子。根据一张图纸(类),我们可以设计出成千上万的房子(类对象),每个房子长相都是类似的(都有相同的类变量和类方法),但它们都有各自的主人,那么如何对它们进行区分呢?当然是通过 self 参数,它就相当于每个房子的门钥匙,可以保证每个房子的主人仅能进入自己的房子(每个类对象只能调用自己的类变量和类方法)。也就是说,同一个类可以产生多个对象,当某个对象调用类方法时,该对象会把自身的引用作为第一个参数自动传给该方法。
在上面这段代码中,我们使用Person类创建了两个不同的对象,打印构造方法中的self发现也不同,这一点也恰好印证了:无论是类中的构造函数还是普通的类方法,实际调用它们的谁,则第一个参数 self 就代表谁。
在上面的章节中我们通过生活中的例子,初步认识了对象,学到了如何定义一个类,知道了类中可以包含属性(变量)与方法(函数)。下面我们继续学习在类中定义的变量与函数,加深对python类的认识。
三、变量的划分(类变量、实例变量、局部变量)
类变量是定义在类内,但在函数外部的变量。
实例变量是定义在类的函数体内,并且是以“self.变量名”定义的变量。
局部变量是定义在类的函数体内,但不是以“self.变量名”定义的变量。
3.1类变量
class Person:
name = "sun" # 这是一个类变量
def __init__(self, age):
self.age = age # 这是一个实例变量
def talk(word):
words = "Hi"+word # 这是一个局部变量
print(words)
p1 = Person(12)
p2=Person(18)
#类变量的调用方式
print(Person.name)#调用类变量的第一种方式:类名.变量名
print(p1.name)#调用类变量的第二种方式:对象名.变量名
#类变量为所有实例共享
print(p1.name)
print(p2.name)
#类变量的修改方式
Person.name="张三"#通过类名.变量名的方式对类变量重新赋值
print(Person.name)#类变量的值变为"张三"
print(p1.name)#用对象p1调用类变量发现值变为"张三"
print(p2.name)#用对象p2调用类变量发现值也变为“张三”
在上面的这段代码中name为类变量。并且类变量具有两种调用方式:(1)类名.变量名(2)对象名.变量名。我们还可以通过类名.变量名的方式对类变量的属性值进行重新赋值修改。但是由于类变量为所有实例对象共享,修改类变量后,通过两个不同的实例对象重新调用name类变量,发现变量值也发生改变。
还有一点需要注意的是,通过类对象是无法修改类变量的。通过类对象对类变量赋值,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量。看如下代码:
class Student:
name="王同学"
def __init__(self,age):
self.age=age
s1=Student(16)
s1.name="王五"
print(s1.name)
print(Student.name)
王五
王同学
不仅可以通过类名.变量名的方式修改类中已有的类变量,还可以动态添加没有的类变量。
在上段代码中添加以下代码:
class Person:
name = "sun"
def __init__(self, age):
self.age = age
p1 = Person(12)
p2=Person(18)
Person.sex="男"
print(Person.sex)
print(p1.sex)
print(p2.sex)
运行结果
男
男
男
3.2 实例变量
下面的代码中age,name为实例变量,其中,由于 __init__() 函数在创建类对象时会自动调用,而 learn() 方法需要类对象手动调用。因此,Person类的类对象都会包含 age实例变量,而只有调用了 learn() 方法的类对象,才包含 name实例变量。它们都可以通过对象名.变量名的方式调用
class Person:
def __init__(self, age):
self.age = age # 这是一个实例变量
def learn(self):
self.name="sun"#这也是一个实例变量
#实例变量调用方式:对象名.变量名
p1=Person(18)
p2=Person(19)
print(p1.age)
print(p2.age)
#p2对象调用learn方法后再调用name属性
p2.learn()
print(p2.name)
#p1对象在没有调用learn方法时直接调用name
print(p1.name)#直接报错
18
19
sun
Traceback (most recent call last):
File "e:\VsCodeWorkSpace\PythonProjects\PythonDemo\vari.py", line 76, in <module>
print(p1.name)#直接报错
AttributeError: 'Person' object has no attribute 'name'
值得注意的是,如果类变量与实例变量同名,它会首选实例变量。看下面的例子
class Student:
name="王同学"
def __init__(self,name):
self.name=name
s1=Student("王五")
print(s1.name)
print(Student.name)
王五
王同学
另外,和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。例如:
class Person:
def __init__(self, age):
self.age = age # 这是一个实例变量
p1=Person(18)
p2=Person(19)
print(p1.age)#18
print(p2.age)#19
#修改p1的age值
p1.age=20
print(p1.age)#20
print(p2.age)#19
可以看出,对p1对象的age修改值,并不会影响到p2的age
3.3 局部变量
下面的代码段中c就是一个定义在函数内部的局部变量
class add:
def sum(self,a,b):
c=a+b
print(c)
s=add()
s.sum(1,2)
需要注意的一点是,局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。
在这一章节,我们对python类中的属性(变量)进行了比较深入的分析,和类属性一样,类方法也可以进行更细致的划分,下面我们按照这个思路继续分析类中的方法(函数)
四、实例方法、静态方法、类方法
采用 @classmethod 修饰的方法为类方法;采用 @staticmethod 修饰的方法为静态方法;不用任何修改的方法为实例方法
4.1 实例方法
class math:
def sum(self,var1,var2):
return var1+var2
上面这段代码中的sum方法就是一个实例方法,实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象。实例方法通常可以用对象直接调用,也可以使用类名调用(但此方式需要手动给 self 参数传值)
class Math:
def sum(self,var1,var2):
return var1+var2
m=Math()
print(m.sum(2,3))#使用对象调用实例方法
print(Math.sum(m,2,6))#使用类名调用实例方法,但是必须传入对象本身
值得注意的是,类的构造方法也是一种实例方法。
4.2 类方法
与实例方法相似,类方法最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象)。也就是说,我们在调用类方法时,无需显式为 cls 参数传参。
调用类方法的方式也有两种:(1)使用类名.方法名(2)使用对象名.方法名
class Math:
@classmethod
def sum(cls,var1,var2):
return var1+var2
print(Math.sum(2,6))#使用类名调用类方法,但是不必显式传入类本身
#也可以使用对象调用类方法
m = Math()
print(m.sum(2,3))
值得注意的是,类方法需要使用@classmethod修饰符进行修饰,上面的sum方法如果不加@classmethod,python会将该方法认定为实例方法
4.3 静态方法
静态方法需要使用@staticmethod修饰,它没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。
class Math:
@staticmethod
def sum(var1,var2):
return var1+var2
print(Math.sum(2,6))#使用类名调用静态方法
#也可以使用对象调用静态方法
m = Math()
print(m.sum(2,3))
到此,通过以上所有的章节我们系统地认识了python对象、类、类的属性、类的方法,有了这些基础,接下来我们继续探讨python封装继承与多态,由于篇幅有限,后续章节会在下一篇博客中介绍。