面向对象最重要的概念就是类
(Class)和实例
(Instance)有些人也将实例
称之为对象
。对象
和实例
个人认为是同一个意思。
对象:个人理解对象
就是一个具体的事物,比如,一名学生、一张桌子、一张凳子、一个笔记本都可以看成是一个对象。
类:个人理解类
是对对象
的一个抽象化,如,定义一个Students
类,类里面有name
、age
、sex
等,那么通过这个类可以创建出许多个不同的实例
{'name':'小米','age':20,'sex':'男'}
{'name':'小王','age':10,'sex':'女'}
{'name':'小黑','age':30,'sex':'女'}
这三个对象都有不同的名称
、年龄
、性别
。但是他们有相同的属性name
、age
、sex
。
1.类的定义
python中类定义是用关键字class
定义,类名的首字母要大写
。类最基本的功能就是封装。
class Students():
pass
class StudentHomework():
pass
最简单的类
class Student():
name = '小米'
age = 0
def do_homework(self):
print('正在做家庭作业')
#实例化类
student1 = Student()
#调用类的方法
student1.do_homework()
以上大概就是创建一个类并实例化
2.函数与方法的区别
python中定义函数
和方法
都是通过关键字def
来定义的,在类中定义的函数习惯性的称之为方法
。方法是倾向于设计层面
而函数是倾向于面向过程
。两者更多是概念上的区分。
函数:函数是可重复使用的,用来实现单一或相关联功能的代码段,函数是一个面向过程的编程。
方法:在类中定义的函数习惯性称之为方法,方法是设计层面的一个概念。
3.类和对象的关系
类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。个人理解:类是对象的一个抽象,对象就是实例化类。
设计一个类是否优美,取决于设计者能否抓住类的特征
(类变量) 与 行为
(方法)
类和对象的关系就是实例化
#实例化
student1 = Student()
4.构造函数
__init__()
方法是类的一种特殊的方法,被称为类的构造函数
或初始化方法
,当创建类的实例时就会自动
调用该方法。构造方法一般用于初始化。
class Student():
name = '小米'
age = 0
#构造函数
def __init__(self,name,age):
self.name = name
slef.age = age
print('创建一个名为:' + self.name + '的同学,年龄是:' + str(self.age))
#方法
def do_homework(self):
print('正在做家庭作业')
#实例化
student1 = Student('小名',20)
注意:类中的方法需要传self,self名称是官方建议的,但不是绝对的
构造函数和方法区别:
- 构造函数是不能
return None
值之外的值 - 构造函数用于初始化,方法用于定义类的
行为
5.区别类变量和实例变量
实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法:
class Dog:
#类变量
kind = 'canine'
def __init__(self,name):
#实例变量
self.name = name
>>> d = Dog('fido')
>>> e = Dog('buddy')
>>> d.kind
'canine'
>>> e.kind
'canine'
>>> d.name
'fido'
>>> e.name
'buddy'
类变量:类变量是和类关联在一起的变量,类变量放在类下面,如上面kind
就是一个类变量
实例变量:实例变量是和实例关联在一起的变量,通过self
将实例和变量关联起来。如上面name
就是一个实例变量
6.类与对象的变量查找顺序
在Python程序中创建、改变、查找变量名时,都是在一个保存变量名的空间中进行,我们称之为命名空间,也被称之为作用域。python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。变量查找顺序,局部变量->全局变量->内置变量
只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。
python在类中查找变量的顺序:实例变量->类变量->父类变量
class Student:
name = '小三'
def __init__(self,name):
name = name
>>> student1 = Student('小王')
>>> print(student1.name)
'小三'
上面代码中,我们只定义Student
类变量name
,没有定义实例变量,当创建student1
实例时,我们尝试能不能打印出实例变量name
。结果输出了小三
。其实student1实例中并没有name
变量,当我们打印实例变量name时,python在实例中找不到变量就会在类中查找变量name
,如果找到就返回,找不到就会向父类查找。
为了验证student1
实例中真的没有name
变量,我们打印一下实例的所有变量
>>> print(student1.__dict__)
{}
我们将name修改为实例变量
class Student:
name = '小三' #类变量
def __init__(self, name):
self.name = name
>>> student1 = Student('小王')
>>> print(student1.name)
'小王'
>>> print(student1.__dict__)
{name:'小王'}
7.self与实例方法
在类下面定义一个方法,这个方法如果是实例方法,那么需要在该方法的参数第一个放上固定的self
(其他名称也行,官方建议是self),self
是python官方固定的,它和其它自定义参数不同,在实例化时,python会将实例传进去,在调用时不需要传递,官方默认帮我们传递了。
在其他语言中,其实也存在self,只不过是隐式的,但是在python中是必须显式声明的,这点开发者需要注意。
//js
class Student{
//构造方法
constructor(){
this.name = '小三'
}
doHomework(){
print(this.name + '在做家庭作业')
}
}
这里的this不需要去声明,因为js中自动帮我们声明了,python中必须要手动声明self
,self就是指的是该实例自身
8.实例中访问类变量
在实例方法中访问实例变量是通过self.变量名
来访问变量。那么如何在实例中如何访问类变量呢?
通过self.class.[变量名]访问类变量
class Student:
name = '小三'
def do_homework(self):
print('名称是:' + self.__class__.name)
>>> d1 = Student()
>>> d1.do_homework()
'名称是:小三'
通过类名访问类变量
class Student:
name = '小三'
def do_homework(self):
print('名称是:' + Student.name)
>>> d1 = Student()
>>> d1.do_homework()
'名称是:小三'
强烈建议:通过方法来访问类变量,不要通过类来操作类变量。
9.类方法
类方法主要是用来描述类的行为,类方法定义
class Student:
sum = 0
#构造行数
def __init__(self):
print('构造函数')
#类方法
@classmethod #装饰器
def plus_sum(cls):
pass
在实例方法中传入第一个参数self
,而在类方法中传入的第一个参数cls
。区分类方法和实例方法是看方法上面有没有@classmethod
类方法操作类变量
在类方法中直接可以通过cls
来读取类变量,而在实例方法中需要self.__class__
来读取变量
class Student:
sum = 0
def __init__(self):
pass
@classmethod
def plus_sum(cls):
cls.sum += 1
>>> d1 = Student()
# 通过实例调用类方法
>>> d1.plus_sum()
# 通过类调用类方法
>>> Student.plus_sum()
10.静态方法
类方法的定义,静态方法不需要传入一个固定名称的参数,静态方法需要用@staticmethod
来声明是静态方法
class Student:
sum = 0
def __init__(self):
pass
@staticmethod
def add():
# 静态方法 对类变量的读取
print(Student.sum)
print('这是一个静态方法')
>>> d1 = Student()
# 实例调用静态方法
d1.add()
# 类调用静态方法
Student.add()
类的静态方法可以被实例或类调用
11.私有性
成员的可见性是指变量或方法是否能被访问,类的变量或方法都存在外部访问和内部访问。私有变量
只能在实例中访问。
外部访问:指在类的外部调用,内部访问:指在类的内部调用。
其他语言中,成员的可见性是通过明显的标记来标明,如java中就是通过public
、private
标明方法或变量是公开的
、私有的
。在python类中,表示一个实例或 变量公开还是私有是用双下划线
(__)开头的。如果一个实例方法或变量不是以双下划线开头的,那么就表示这个实例或变量是公开的,反之是私有的。
class Student:
#构造方法
def __init__(self,name,age):
self.name = name
self.age = age
#私有变量
self.__sex = '男'
#私有方法
def __get_name(self):
print(self.name +"性别是:" + self.__sex)
#公开的
def do_homework(self):
print(self.name + "在做家庭作业")
>>> d1 = Student('小王',19)
>>> print(d1.name)
'小王'
>>> print(d1.__sex)
'会报错,因为__sex变量是私有变量只能在内部访问'
>>> print(d1.do_homework())
'小王在做家庭作业'
>>> print(d1.__get_name())
'会报错'
__init__构造方法为什么会公开的呢?
上面代码我们在方法
或者变量
前面加了__
使其成员的可见性变为私有。那么可能有小伙伴们想到__init__
构造函数前面不是也有__
,为什么构造函数是公开的呢?
小菜在介绍构造函数时说构造函数
是一种特殊的方法。细心的小伙伴们就注意到__init__
构造函数不仅开头有__
,结尾也有__
。在python中,默认以__
开头方法或变量都会认为是私有的。但是有一种是除外的。
注意:python不会认为以双划线开头 并且 以双下划线结尾的变量 或 方法是私有的。构造函数就属于这种,所以构造函数就不是私有的
通过实例添加变量
python中可以通过实例添加变量
class Student:
def __init__(self,name,age)
self.name = name
self.age = age
self.__score = 69
>>> d1 = Student('小王',19)
>>> d1.__score = 70
>>> print(d1.__score)
70
如果小伙们读了前面介绍的私有性,再看这段代码,肯定心里一万个cnm
在崩腾,不是说私有变量不能在外部被访问吗?怎么现在居然可以赋值?请听小菜一本正经的瞎说:这段代码中d1.__score = 70
最后打印出了d1.__score
为70。其实这里的__score
不是私有变量,而__init__
构造函数的__score
是私有变量。估计有小伙伴们要懵逼了。
为了演示python是如何操作的,小菜简化代码
class Student:
def __init__(self,name,age)
self.name = name
self.age = age
self.__score = 69
>>> d1 = Studnet('小王',19)
>>> print(d1.__score)
'这条语句会报错,原因__score是 私有变量'
>>> print(d1.__dict__)
{'name': '小王', 'age': 19, '_Student__score': 69}
这里我们看到多了一个_Student__score
,这是为什么呢?在实例化时,python会将私有变量转换成_Student
后面再加变量名。所以当我们在输出d1.__score
时就会报错,报一个__score
变量不存在。
回过头我们在说这段代码
class Student:
def __init__(self,name,age)
self.name = name
self.age = age
self.__score = 69
>>> d1 = Student('小王',19)
>>> d1.__score = 70
>>> print(d1.__score)
70
>>> print(d1.__dict__)
{'name': '小王', 'age': 19, '_Student__score': 69, '__score':70}
通过d1.__dict__
打印出来实例的所有变量,我们可以分析出d1.__score=70
就是给实例定义了一个__score
变量,但__score
不是私有的,在构造函数中的__score
已经在实例化时被转换成_Student__score
12.python的访问私有变量
在上面我们谈了python的私有性,实际上在实例化时将原来的私有变量做了一层命名空间的转换。那么我们如何访问私有变量呢?
class Student:
def __init__(self,name,age)
self.name = name
self.age = age
self.__score = 69
>>> d1 = Student('小王',19)
>>> print(d1._Student__score)
69
13.继承
类的继承最基本的作用就是为了避免定义重复的变量或方法。
文件结构
./
|-hu.py
|-st.py
hu.py
class Human():
sum = 0
def __init__(self,name,age):
self.name = name
self.age = age
def get_name(self):
print(self.name)
st.py
from hu import Human
class Student(Human):
def do_homework(self):
print('调用Student类的方法')
#子类调用父类的sum
>>> print(Student.sum)
0
>>> d1 = Student('小王',19)
>>> print(d1.name) #实例调用name
'小王'
>>> print(d1.get_name()) #实例调用父类的方法
'小王'
14.子类方法调用父类方法
./
|-hu.py
|-st.py
hu.py
class Human():
sum = 0
def __init__(self,name,age):
self.name = name
self.age = age
print("父类初始化方法",name, age)
def get_name(self):
print(self.name)
st.py
from hu import Human
class Student(Human):
def __init__(self, school, name,age):
self.school = school
#父类初始化
#Human.__init__(self,name,age)
#通过 super关键字调用父类
super().__init__(name,age)
def do_homework(self):
print('调用Student类的方法')
stu = Student('上海大学','wali',18)
15.总结