Python: 面向对象编程(类和对象)
文章背景: 最近在学习课程Python-Core-50-Courses,其中有个章节是面向对象编程,涉及的内容是类(class)和对象。下面对所学的内容进行相应的整理。
1 定义类
2 创建和使用类
3 初始化方法
4 打印对象
5 可见性和属性封装器
6 动态属性
7 静态方法和类方法
8 继承和多态
1 定义类
class Student:
def study(self, course_name):
print(f'学生正在学习{course_name}.')
def play(self):
print(f'学生正在玩游戏.')
(1) 在Python中,可以使用class
关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。
(2) 写在类里面的函数我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self
,它代表了接收这个消息的对象本身。
(3) f-string采用 {content:format}
设置字符串格式,其中 content
是替换并填入字符串的内容,可以是变量、表达式或函数等,format
是格式描述符。采用默认格式时不必指定 {:format}
。
2 创建和使用类
stu1 = Student()
stu2 = Student()
print(stu1)
print(stu2)
print(hex(id(stu1)), hex(id(stu2)))
<__main__.Student object at 0x000002861BBD7C48>
<__main__.Student object at 0x000002861BBE1388>
0x2861bbd7c48 0x2861bbe1388
(1) 当我们用print
函数打印stu1
和stu2
两个变量时,我们看到输出了对象在内存中的地址(十六进制形式),跟我们用id
函数(同时借助hex函数)查看对象标识获得的值是相同的。
(2) 我们定义的变量其实保存的是一个对象在内存中的逻辑地址(位置),通过这个逻辑地址,我们就可以在内存中找到这个对象。所以stu3 = stu2
这样的赋值语句并没有创建新的对象,只是用一个新的变量保存了已有对象的地址。
# 通过“类.方法”调用方法,第一个参数是接收消息的对象,第二个参数是学习的课程名称
Student.study(stu1, 'Python程序设计')
# 通过“对象.方法”调用方法,点前面的对象就是接收消息的对象,只需要传入第二个参数
stu1.study('Python程序设计')
Student.play(stu2) # 学生正在玩游戏.
stu2.play() # 学生正在玩游戏.
学生正在学习Python程序设计.
学生正在学习Python程序设计.
学生正在玩游戏.
学生正在玩游戏.
3 初始化方法
class Student:
"""学生"""
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}.')
def play(self):
"""玩耍"""
print(f'{self.name}正在玩游戏.')
# 由于初始化方法除了self之外还有两个参数
# 所以调用Student类的构造器创建对象时要传入这两个参数
stu1 = Student('骆昊', 40)
stu2 = Student('王大锤', 15)
stu1.study('Python程序设计') # 骆昊正在学习Python程序设计.
stu2.play() # 王大锤正在玩游戏.
骆昊正在学习Python程序设计.
王大锤正在玩游戏.
(1) 在我们调用Student
类的构造器创建对象时,首先会在内存中获得保存学生对象所需的内存空间,然后通过自动执行__init__
方法,完成对内存的初始化操作,也就是把数据放到内存空间中。
(2) 我们可以通过给Student
类添加__init__
方法的方式为学生对象指定属性,同时完成对属性赋初始值的操作,正因如此,__init__
方法通常也被称为初始化方法。
4 打印对象
在Python中,以两个下划线__
(读作dunder
)开头和结尾的方法通常都是有特殊用途和意义的方法,我们一般称之为魔术方法或魔法方法。如果我们在打印对象的时候不希望看到对象的地址而是看到我们自定义的信息,可以通过在类中放置__repr__
魔术方法来做到,该方法返回的字符串就是用print
函数打印对象的时候会显示的内容。
class Student:
"""学生"""
def __init__(self, name, age):
"""初始化方法"""
self.name = name
self.age = age
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}.')
def play(self):
"""玩耍"""
print(f'{self.name}正在玩游戏.')
def __repr__(self):
return f'{self.name}: {self.age}'
stu1 = Student('骆昊', 40)
print(stu1)
students = [stu1, Student('李元芳', 36), Student('王大锤', 25)]
print(students)
骆昊: 40
[骆昊: 40, 李元芳: 36, 王大锤: 25]
除了使用__repr__()
魔术方法打印对象,还可以使用__str__()
方法。如果类中同时存在__repr__()
和__str__()
时,print()执行时调用的是__str__()
,而不是__repr__()
。
__str__()
的目标客户是用户,__repr__()
的目标客户则是开发者。
from datetime import date
a = date.today() # a是日期类型的一个实例,赋值为今日的日期
b = str(a) # 将a转成字符串后赋给b,b是字符串类型的一个实例
print(a.__str__()) # 输出:2022-04-20
print(b.__str__()) # 输出:2022-04-20
# 可见调用日期对象a和字符串对象b的__str__()输出结果是一样的,只看输出结果是看不出a和b其实是不同类的实例的
print(a.__repr__()) # 输出:datetime.date(2022, 4, 20),看出是日期类型
print(b.__repr__()) # 输出:'2022-04-20',看出是字符串类型
# 从a和b的__repr__()可以看出a和b是不同类的对象,所以说__repr__()的输出结果是给开发者看的
print(a) # 输出:2022-04-20
print(b) # 输出:2022-04-20
# 这两条语句同print(a.__str__())和print(b.__str__())
__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
5 可见性和属性封装器
在Python中,可以通过给对象属性名添加前缀下划线的方式来说明属性的访问可见性,例如,可以用__name
表示一个私有属性,_name
表示一个受保护属性。
class Student:
def __init__(self, name, age):
self.__name = name
self.__age = age
def study(self, course_name):
print(f'{self.__name}正在学习{course_name}.')
stu = Student('王大锤', 20)
stu.study('Python程序设计')
print(stu.__name)
王大锤正在学习Python程序设计.
AttributeError: 'Student' object has no attribute '__name'
以__
开头的属性__name
是私有的,在类的外面无法直接访问,但是在类里面的study
方法可以通过self.__name
访问该属性。
需要提醒大家的是,Python并没有从语法上严格保证私有属性的私密性,它只是给私有的属性和方法换了一个名字来阻挠对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,我们可以对上面的代码稍作修改就可以访问到私有的属性。
class Student:
def __init__(self, name, age):
self.__name = name
self.__age = age
def study(self, course_name):
print(f'{self.__name}正在学习{course_name}.')
stu = Student('王大锤', 20)
stu.study('Python程序设计')
print(stu._Student__name, stu._Student__age)
王大锤正在学习Python程序设计.
王大锤 20
Python中可以通过property
装饰器为私有
属性提供读取和修改的方法。装饰器通常会放在类、函数或方法的声明之前,通过一个@
符号表示将装饰器应用于类、函数或方法。
class Student:
def __init__(self, name, age):
self.__name = name
self.__age = age
# 属性访问器(getter方法) - 获取__name属性
@property
def name(self):
return self.__name
# 属性修改器(setter方法) - 修改__name属性
@name.setter
def name(self, name):
# 如果name参数不为空,就赋值给对象的__name属性
# 否则将__name属性赋值为'无名氏',有两种写法
# self.__name = name if name else '无名氏'
self.__name = name or '无名氏'
@property
def age(self):
return self.__age
stu = Student('王大锤', 20)
print(stu.name, stu.age) # 输出:王大锤 20
stu.name = ''
print(stu.name) # 输出:无名氏
# stu.age = 30 # AttributeError: can't set attribute
6 动态属性
在Python中,我们可以动态地为对象添加属性,这是Python作为动态类型语言的一项特权。
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
stu = Student('王大锤', 20)
# 为Student对象动态添加sex属性
stu.sex = '男'
print(stu.sex) # 输出:男
如果不希望在使用对象时动态地为对象添加属性,可以使用Python的__slots__
魔法方法。对于Student
类来说,可以在类中指定__slots__ = ('name', 'age')
,这样Student
类的对象只能有name
和age
属性,如果想动态添加其他属性将会引发异常。
class Student:
__slots__ = ('name', 'age')
def __init__(self, name, age):
self.name = name
self.age = age
stu = Student('王大锤', 20)
stu.sex = '男' # AttributeError: 'Student' object has no attribute 'sex'
7 静态方法和类方法
class Triangle(object):
"""三角形类"""
def __init__(self, a, b, c):
"""初始化方法"""
self.a = a
self.b = b
self.c = c
@staticmethod
def is_valid(a, b, c):
"""判断三条边长能否构成三角形(静态方法)"""
return a + b > c and b + c > a and a + c > b
# @classmethod
# def is_valid(cls, a, b, c):
# """判断三条边长能否构成三角形(类方法)"""
# return a + b > c and b + c > a and a + c > b
def perimeter(self):
"""计算周长"""
return self.a + self.b + self.c
def area(self):
"""计算面积"""
p = self.perimeter() / 2
return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5
print(Triangle.is_valid(3,4,5))
True
上面的代码使用staticmethod
装饰器声明了is_valid
方法是Triangle
类的静态方法,如果要声明类方法,可以使用classmethod
装饰器。可以直接使用类名.方法名
的方式来调用静态方法和类方法,二者的区别在于,类方法的第一个参数是类对象本身,而静态方法则没有这个参数。
对象方法、类方法、静态方法都可以通过类名.方法名
的方式来调用,区别在于方法的第一个参数到底是普通对象还是类对象,还是没有接受消息的对象。静态方法通常也可以直接写成一个独立的函数,因为它并没有跟特定的对象绑定。
8 继承和多态
class Person:
"""人类"""
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f'{self.name}正在吃饭.')
def sleep(self):
print(f'{self.name}正在睡觉.')
class Student(Person):
"""学生类"""
def __init__(self, name, age):
# super(Student, self).__init__(name, age)
super().__init__(name, age)
def study(self, course_name):
print(f'{self.name}正在学习{course_name}.')
class Teacher(Person):
"""老师类"""
def __init__(self, name, age, title):
# super(Teacher, self).__init__(name, age)
super().__init__(name, age)
self.title = title
def teach(self, course_name):
print(f'{self.name}{self.title}正在讲授{course_name}.')
stu1 = Student('白元芳', 21)
stu2 = Student('狄仁杰', 22)
teacher = Teacher('武则天', 35, '副教授')
stu1.eat()
stu2.sleep()
teacher.teach('Python程序设计')
stu1.study('Python程序设计')
白元芳正在吃饭.
狄仁杰正在睡觉.
武则天副教授正在讲授Python程序设计.
白元芳正在学习Python程序设计.
(1) 继承的语法是在定义类的时候,在类名后的圆括号中指定当前类的父类。如果定义一个类的时候没有指定它的父类是谁,那么默认的父类是object
类。object
类是Python中的顶级类,这也就意味着所有的类都是它的子类,要么直接继承它,要么间接继承它。
(2) 在子类的初始化方法中,我们可以通过super().__init__()
来调用父类的初始化方法。
(3) 子类继承父类的方法后,还可以对方法进行重写(重新实现该方法),不同的子类可以对父类的同一个方法给出不同的实现版本,这样的方法在程序运行时就会表现出多态行为(调用相同的方法,做了不同的事情)。
参考资料:
[1] 面向对象编程入门(https://github.com/jackfrued/Python-Core-50-Courses/blob/master/%E7%AC%AC17%E8%AF%BE%EF%BC%9A%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8.md)
[2] 特殊方法函数(http://pythonabc.org/index.php/python-basic/2018-05-12-13-14-43/91-3-9-7-class)
[3] python字符串格式化深入详解(四种方法)(https://blog.csdn.net/qq_27825451/article/details/105652244)
[4] 定制类(https://www.liaoxuefeng.com/wiki/1016959663602400/1017590712115904)
相关文章
- 在pycharm中如何新建Python文件?_github下载的python源码项目怎么用
- python十进制转换_Python 进制转换
- python进制转换函数-Python中进制转换函数的使用
- Python入门系列(五)一篇搞懂python语句
- Python中if __name__ == ‘__main__‘:的作用和原理
- Python中字符串的Format用法。
- python zipfile_Python 学习入门(16)—— zipfile
- python中copy.deepcopy_Python eval
- python中if判断语句的用法_Python if判断语句的用法详细介绍[通俗易懂]
- python设置时间过期改变状态_Python Redis设置过期时间「建议收藏」
- Python实现久坐提醒小助手程序「建议收藏」
- 纯 Python 实现的图片压缩工具
- Python面向对象编程-类和对象-静态方法和类方法
- Python面向对象编程-类和对象-接口
- Python 元学习实用指南:1~5
- Python 扫描IP段 指定端口是否开放详解编程语言
- python通过MySQLdb访问mysql数据库详解编程语言
- Python生成器、迭代器、可迭代对象详解编程语言
- 使用Python执行Linux命令的方法(python调用linux命令)
- Python与MongoDB 无缝连接(python连接mongodb)
- Python连接MySQL数据库:初学者指南(python入mysql)