一:什么是类
类即类别/种类,是面向对象分析和设计的基石,如果多个对象有相似的数据与功能,那么该多个对象就属于同一种类。有了类的好处是:我们可以把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可,极大地节省了空间。所以,如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器。
类:可以理解成一个模板
实例:根据模版创建出来的一个对象
属性:这类东西具有的特征,例如:汽车的重量,品牌
方法:这类东西可以做的事情,例如:汽车可以刹车
必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)
二、类的定义与实例化
使用关键字class定义类
将共有属性提取到:init初始化函数中
定义共有的方法
class Student:
school = '华阳中学'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def choose(self):
print(f'{self.name} is choosing a course')
调用类的过程就是类的实例化,拿到的返回值就是程序中的一个对象,或称为一个实例
stu1 = Student("json", 20, "男") # 每实例化一次Student类就得到一个学生对象
stu2 = Student("lili", 21, "女")
stu3 = Student("jack", 19, "男")
单拿stu1的产生过程来分析,调用类会先产生一个空对象stu1,然后将stu1连同调用类时括号内的参数一起传给Student.__init__(stu1,"json", 20, "男")
def __init__(self, name, age, sex):
self.name = name # stu1.name = 'json'
self.age = age # stu1.age = 20
self.sex = sex # stu1.sex = '男'
会产生对象的名称空间,同样可以用__dict__查看
print(stu1.__dict__)
# {'name': 'json', 'age': 20, 'sex': '男'}
我们造出了三个对象(实例)与一个类,对象(实例)存放各自独有的数据,类中存放对象(实例)们共有的内容。
三、访问属性
3.1:类属性与实例属性
在类中定义的名字,都是类的属性,分为两种:数据属性和函数属性。可以通过__dict__访问属性的值,比如Student.__dict__[‘school’],但Python提供了专门的属性访问语法。
print(Student.school) # 访问数据属性,等同于Student.__dict__[‘school’]
# 华阳中学
print(Student.choose) # 访问函数属性,等同于Student.__dict__[‘choose’]
# <function Student.choose at 0x100588940>
操作对象属性也是一样的
print(stu1.name) # 查看,等同于stu1.__dict__[‘name’]
# json
stu1.course = '语文' # 新增,为stu1新增course属性,等同于stu1.__dict__[‘course’] = '语文'
print(stu1.course) # 语文
stu1.age = 22 # 修改,修改stu1的age属性,等同于stu1.__dict__[‘age’] = 22
print(stu1.age) # 22
del stu1.course # 删除,删除stu1的course属性
print(stu1.course) # 抛异常:AttributeError: 'Student' object has no attribute 'course'
当类的数据属性为不可变类型(字符串、数字、元组)时,通过修改实例对象的数据属性的值,不会修改类的数据属性的值,也不会修改其他实例对象属性的值;通过类去修改数据属性的值,会改变所有实例对象的属性的值;
当类的数据属性为可变类型(列表、字典、集合)时,修改实例对象的数据属性的值,会修改类的数据属性的值,同时也会修改其他实例对象属性的值;通过类去修改数据属性的值,会改变所有实例对象的属性的值;
class Student:
school = '华阳中学'
course = []
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def choose(self):
print(f'{self.name} is choosing a course')
stu1 = Student("json", 20, "男") # 每实例化一次Student类就得到一个学生对象
stu2 = Student("lili", 21, "女")
修改实例对象中不可变的类属性:
print(Student.school) # 华阳中学
print(stu1.school) # 华阳中学
print(stu2.school) # 华阳中学
stu1.school = '成都七中'
print(Student.school) # 华阳中学
print(stu1.school) # 成都七中
print(stu2.school) # 华阳中学
通过类修改不可变的类属性:
print(Student.school) # 华阳中学
print(stu1.school) # 华阳中学
print(stu2.school) # 华阳中学
Student.school = '成都七中'
print(Student.school) # 成都七中
print(stu1.school) # 成都七中
print(stu2.school) # 成都七中
修改实例变量中可变类型的值:
print(Student.course) # ['语文']
print(stu1.course) # ['语文']
print(stu2.course) # ['语文']
stu1.course.append('数学')
print(Student.course) # ['语文', '数学']
print(stu1.course) # ['语文', '数学']
print(stu2.course) # ['语文', '数学']
通过类修改可变类型的类属性:
print(Student.course) # ['语文']
print(stu1.course) # ['语文']
print(stu2.course) # ['语文']
Student.course.append('英语')
print(Student.course) # ['语文', '英语']
print(stu1.course) # ['语文', '英语']
print(stu2.course) # ['语文', '英语']
3.2:属性查找顺序与绑定方法
对象的名称空间里只存放着对象独有的属性,而对象们相似的属性是存放于类中的。对象在访问属性时,会优先从对象本身的__dict__中查找,未找到,则去类的__dict__中查找。
1、类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址。
print(id(Student.school))
# 4376459376
print(id(stu1.school))
# 4376459376
print(id(stu2.school))
# 4376459376
2、类中定义的函数是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数。
Student.choose(stu1)
# json is choosing a course
Student.choose(stu2)
# lili is choosing a course
但其实类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同。
print(id(Student.choose))
# 4368304448
print(id(stu1.choose))
# 4368328576
print(id(stu2.choose))
# 4368328576
绑定到对象的方法特殊之处在于,绑定给谁就应该由谁来调用,谁来调用,就会将’谁’本身当做第一个参数自动传入(方法__init__也是一样的道理)
stu1.choose() # 等同于Student.choose(stu1)
# json is choosing a course
stu2.choose() # 等同于Student.choose(stu2)
# lili is choosing a course
注意:绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但命名为self是约定俗成的。