Python 面向对象程序设计
目录
- Python 面向对象程序设计
- 1 面向过程编程
- 2 面向对象编程
- 3. 面向过程和面向对象的优缺点
- 4. 由浅入深了解面向对象
- 4.1 学生选课为例
- 4.2 类和对象
- 4.3 属性的查找顺序
- 5. 面向对象绑定方法
- 5.1 小案例:计算一共产生了多少个对象
- 5.2 绑定方法
- 5.3 静态方法(非绑定方法)
- 6. 隐藏属性
- 7. property装饰器
- 未完待续
1 面向过程编程
面向过程——Procedure Oriented,是一种以过程为中心的编程思想,它首先分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,在使用时依次调用,是一种基础的顺序的思维方式。面向过程开发方式是对计算机底层结构的一层抽象,它将程序分为数据和操纵数据的操作两部分,其核心问题是数据结构和算法的开发和优化。
最典型的就是流水线,上一道工序完成后,后面的流程才能进行。
-
面过程编程的特点:
功能模块化,代码流程化
-
优点:
性能高,适合资源紧张、实时性强的场合
-
缺点:
不易复用和不易扩展
2 面向对象编程
面向对象编程——Object Oriented Programming,简称OOP
,是一种程序设计思想。OOP
把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
3. 面向过程和面向对象的优缺点
面向对象 | 面向过程 | |
---|---|---|
特性 | 抽象、继承、封装、多态 | 功能模块化,代码流程化 |
优点 | 易维护、易复用、易扩展、低耦合 | 性能高,适合资源紧张、实时性强的场合 |
缺点 | 性能比面向过程低 | 没有面向对象易维护、易复用、易扩展 |
4. 由浅入深了解面向对象
4.1 学生选课为例
"""
1, 要定义学生的信息: 姓名,性别,年龄,学校,所选课程
"""
# 学生的特征:
student_name = 'Hans'
student_gender = 'M'
student_age = 20
shool = '社会大学'
student_course = []
# 学生的功能:选课
def course_selection_system(name, gender, age, shool, course):
student_course.append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (name, gender, age, shool, student_course))
# 执行结果:
course_selection_system(student_name, student_gender, student_age, shool, "如何接受社会的毒打")
学生:Hans 性别:M 年龄:20 在社会大学 期间所选课程:['如何接受社会的毒打']
"""
2, 把学生的信息定义成一个字典
"""
student1 = {
'student_name': 'Hans',
'student_gender': 'M',
'student_age': 20,
'shool':'社会大学',
'student_course':[]
}
def course_selection_system(student, course):
student1['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (student['student_name'], student['student_gender'], \
student['student_age'], student['shool'], student['student_course']))
#执行结果:
course_selection_system(student1, "如何接受社会的毒打")
学生:Hans 性别:M 年龄:20 在社会大学 期间所选课程:['如何接受社会的毒打']
"""
3, 把选课的函数放到字典里, 某个学生要选课成功,里面要包括他的特征和技能,
这样一个学生他的特征和功能都有了
"""
def course_selection_system(student, course):
student1['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (student['student_name'], student['student_gender'], \
student['student_age'], student['shool'], student['student_course']))
student1 = {
'student_name': 'Hans',
'student_gender': 'M',
'student_age': 20,
'shool':'社会大学',
'student_course':[],
'course_select':course_selection_system
}
student2 = {
'student_name': 'Jack',
'student_gender': 'M',
'student_age': 20,
'shool':'社会大学',
'student_course':[],
'course_select':course_selection_system
}
student1['course_select'](student1, "五块钱如何花一星期")
student2['course_select'](student2, "如何接受社会的毒打")
#执行结果:
学生:Hans 性别:M 年龄:20 在社会大学 期间所选课程:['五块钱如何花一星期']
学生:Jack 性别:M 年龄:20 在社会大学 期间所选课程:['如何接受社会的毒打'
# 这是一两个学生,如果是十个,百个或更多都要手写一遍???
# 这时就需要面向对象了。
"""
对象是什么
1. 程序中:
函数:盛放数据的容器
对象:盛放数据和函数的容器
2. 现实生活中:
一切皆对象
对象:特征与技能的结合体
"""
4.2 类和对象
"""
对象: 特征与技能的结合体
类:一系列对象相似的特征与相似的技能的结合体
站在不同的分类,划分的分类不一定一样
在程序中先有类再有对象。现实中先有对象,再有类
"""
# 把上面的程序 相同的部分拿出来,
student1 = {
'student_name': 'Hans',
'student_gender': 'M',
'student_age': 20,
'shool':'社会大学',
'student_course':[],
'course_select':course_selection_system
}
student2 = {
'student_name': 'Jack',
'student_gender': 'M',
'student_age': 20,
'shool':'社会大学',
'student_course':[],
'course_select':course_selection_system
}
"""
这个程序中有哪些是相同的:
学校(shool)和选课功能(course_selection_system)
可以把这两个功能提取出来
"""
student_public = {
'shool':'社会大学',
'course_select':course_selection_system
}
# 这个提取出来的公共的功能就是类,但在python中写有专业的写法:
class 类名():
pass
# class为关键字
# 类名一般首字母大写
# 代码:
class student_public():
shool = '社会大学'
def course_selection_system(student, course):
student1['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (student['student_name'], student['student_gender'], \
student['student_age'], student['shool'], student['student_course']))
# 类在定义的时候会执行,这是与函数不同的地方(函数在定义时不会执行,只有在调用时执行)
# 查看类的名称空间:
print(student_public.__dict__)
{'__module__': '__main__', 'shool': '社会大学', 'course_selection_system': , '__dict__': , '__weakref__': , '__doc__': None}
# 类的底层为字典
"""
类在定义时做了哪些:
1. 类在定义时会立即执行
2. 产生一个类的名称空间,然后把类体里执行的数据都放到名称空间里(__dict__返回的字典里)
3. 把类的名称空间绑定给__dict__
变量放到类里面叫属性
函数放到类里面叫方法
"""
# 调用类
stu = student_public()
# 调用类从而产生对象。
# 调用类从而产生对象的过程,叫类的初始化
#查看对象的名称空间
print(stu.__dict__)
# 执行结果:
{} # 一个空字典
# 现在用Python的语法把类定义出来了。
# 但是学生自己独有的特征还没有(自己的名字,自己的年龄等等),刚才看到了为空
# 给对象添加自己独有的特征
stu.__dict__['name'] = 'Hans'
stu.__dict__['gender'] = 'M'
stu.__dict__['age'] = 20
stu.__dict__['course'] = ['五块钱如何花一星期','如何接受社会毒打']
print(stu.__dict__)
# 执行结果:
{'name': 'Hans', 'gender': 'M', 'age': 20, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
# 对象的名称空间为一个空字典,现在添加了一些key,所以也可以对它进行取值
print(stu.__dict__['name'])
Hans
#在Python中一般不使用这种方法赋值和取值,Python中的方法
# 添加
stu.name = 'Hans'
stu.gender = 'M'
stu.age = 20
stu.course = ['五块钱如何花一星期','如何接受社会毒打']
# 取值
print(stu.__dict__)
print(stu.name)
# 执行结果:
{'name': 'Hans', 'gender': 'M', 'age': 20, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
Hans
# 这种方法只能在对象种使用
# 上面的方法依然是很个学生对象都要写一遍,所以这个方法也可以做成函数形式。
# 1. 定义一个类
class student_public():
shool = '社会大学'
def course_selection_system(student, course):
student['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (student['student_name'], student['student_gender'], \
student['student_age'], student['shool'], student['student_course']))
#
# 实例化对象
stu1 = student_public()
# 定义函数
def init(name,gender,age,course):
stu1.name = name
stu1.gender = gender
stu1.age = age
stu1.course = course
init('hans', 'M',20,['五块钱如何花一星期','如何接受社会毒打'])
print(stu1.__dict__)
# 执行结果:
{'name': 'hans', 'gender': 'M', 'age': 20, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
# init()函数中stu1为固定值,如果初始化一个学生,这个函数就不满足了,所以stu1的位置也要传参获得
def init(student,name,gender,age,course):
student.name = name
student.gender = gender
student.age = age
student.course = course
stu1 = student_public()
stu2 = student_public()
init(stu1,'hans', 'M',20,['五块钱如何花一星期','如何接受社会毒打'])
init(stu2,'jack', 'M',18,['五块钱如何花一星期','如何接受社会毒打'])
print(stu1.__dict__)
print(stu2.__dict__)
# 执行结果:
{'name': 'hans', 'gender': 'M', 'age': 20, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
{'name': 'jack', 'gender': 'M', 'age': 18, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
# 这样比较相对比较完美了,但是每次都要手动调用init()这个方法,有没有什么方法可以自动执行init()
# 可以把它写入到类里面,不过要变形
class student_public():
def __init__(student, name, gender, age, course):
student.name = name
student.gender = gender
student.age = age
student.course = course
shool = '社会大学'
def course_selection_system(student, course):
student['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (student['student_name'], student['student_gender'], \
student['student_age'], student['shool'], student['student_course']))
print(student_public.__dict__)
stu1 = student_public()
# 执行结果:
TypeError: student_public.__init__() missing 4 required positional arguments: 'name', 'gender', 'age', and 'course'
# 提示要四个参数,现在给它传四个参数:
stu1 = student_public('hans', 'M',20,['五块钱如何花一星期','如何接受社会毒打'])
print(stu1.__dict__)
# 现在执行不会报错
{'name': 'hans', 'gender': 'M', 'age': 20, 'course': ['五块钱如何花一星期', '如何接受社会毒打']}
# __init__()上面写了五个参数,但是对象在调用的时候只让写四个,因为使用对象调用它会把自己当成第一个参数传递给函数。
# 在类中的方法,类和对象都可以调用,但是类在调用的时候,里面写了几个参数就要传几个参数,但是使用对象时它把自己当成第一个参数传过去,所以推荐使用对象调用
# Python中,因为方法中第一个参数为自己,所以把这个写成self
class student_public():
def __init__(self, name, gender, age, course):
self.name = name
self.gender = gender
self.age = age
self.course = course
shool = '社会大学'
def course_selection_system(self, course):
self['student_course'].append(course)
print("学生:%s 性别:%s 年龄:%s 在%s 期间所选课程:%s" % (self['student_name'], self['student_gender'], \
self['student_age'], self['shool'], self['student_course']))
# 执行:
stu1 = student_public('hans', 'M', 20, '五块钱如何花一星期') # 实例化对象
print(stu1.__dict__)
{'name': 'hans', 'gender': 'M', 'age': 20, 'course': '五块钱如何花一星期'}
# 判断一个
print(isinstance(stu1, student_public))
True
"""
调用类:
1. 调用类(stu1 = student_public())会得到一个空对象,把这个空对象传给student_public.__dict__
2. 调用student_public.__dict__(空对象,'hans', 'M',20,'五块钱如何花一星期')
3. 得到一个初始化结果
4. 在__init__里不能使用return 返回值,如果真要使用也只能返回None
"""
4.3 属性的查找顺序
类属性(在类中定义的):
# 1, 查看类属性
class student_public():
def __init__(student, name, gender, age, course):
student.name = name
student.gender = gender
student.age = age
student.course = course
shool = '社会大学'
stu1 = student_public('hans', 'M',20,'五块钱如何花一星期')
print(stu1.shool)
# 2, 增加一个类属性
stu1.country = 'CN'
print(stu1.__dict__)
{'name': 'hans', 'gender': 'M', 'age': 20, 'course': '五块钱如何花一星期', 'country': 'CN'}
# 3, 修改类属性
stu1.shool = '清华大学'
print(stu1.shool)
清华大学
# 4, 修改类属性
del stu1.shool
对象属性
class student_public():
def __init__(student, name, gender, age, course):
student.name = name
student.gender = gender
student.age = age
student.course = course
shool = '社会大学'
stu1 = student_public('hans', 'M',20,'五块钱如何花一星期')
# 1, 查看
print(stu1.name)
print(stu1.gender)
print(stu1.age)
hans
M
20
# 2, 修改
stu1.age = 18
print(stu1.age)
18
# 3,增加
stu1.result = '优'
print(stu1.__dict__)
{'name': 'hans', 'gender': 'M', 'age': 18, 'course': '五块钱如何花一星期', 'country': 'CN', 'result': '优'}
# 4, 删除
del stu1.result
print(stu1.__dict__)
{'name': 'hans', 'gender': 'M', 'age': 18, 'course': '五块钱如何花一星期', 'country': 'CN'}
属性查找顺序
1. 先在对象中查找,如果对象中没有则去类中查找
class student_public():
def __init__(self, name, gender, age, course):
self.name = name
self.gender = gender
self.age = age
self.course = course
shool = '社会大学'
print(stu1.shool) # 这个shool为类中定义的
社会大学
print(stu1.__dict__['shool']) # 这个会报错,因为在对象stu1中根本没有shool这个属性,shool这个是类的属性,直接这么取是拿不到的,因为指定从对象stu1中的名称空间中取,对象stu1中的名称空间中根本没有。
#所以使用点(.)的方式,如果对象中的名称空间中没有,它会去类的名称空间中找。
class student_public():
def __init__(self, name, gender, age, course):
self.name = name
self.gender = gender
self.age = age
self.shool = "清华大学"
self.course = course
shool = '社会大学'
print(stu1.shool) # 这个shool为对象中定义的
清华大学
5. 面向对象绑定方法
5.1 小案例:计算一共产生了多少个对象
class Count():
count = 0
def __init__(self, name):
self.name = name
Count.count +=1 # 统计次数
s1 = Count('Hello')
s2 = Count('World')
print(Count.count)
# 执行结果:
2
# 类属性修改,所有对象都改。
# 还有一种写法:
class Count():
count = 0
def __init__(self, name):
self.name = name
self.__class__.count +=1 # 统计次数 和Count.count +=1 一样
s1 = Count('Hello')
s2 = Count('World')
print(Count.count)
5.2 绑定方法
绑定方法有两种:
-
绑定给对象
class Students(): def __init__(self,name, age, gender): self.name = name self.age = age self.gender = gender def printInfo(self): print("%s %s %s" % (self.name, self.age, self.gender)) stu = Students("Hans", 18, 'M') stu.printInfo() # 把printInfo方法绑定给对象stu
-
绑定给类
IP = '192.168.1.123' PORT = 3306 class Mysql(): def __init__(self, ip, port): self.ip = ip self.port = port mysql = Mysql(IP, PORT) print(mysql.ip,mysql.port) # 执行结果: 192.168.1.123 3306 # 如果有多个需要写多次。能不能把Mysql(IP, PORT)这个只写一次 # 可以把这个放到一个函数里 IP = '192.168.1.123' PORT = 3306 class Mysql(): def __init__(self, ip, port): self.ip = ip self.port = port def tell(self): mysql = Mysql(IP, PORT) return mysql obj = Mysql(IP, PORT) obj1 = obj.tell() print(obj1.ip,obj1.port) # 写到函数里后,又有一个问题,mysql = Mysql(IP, PORT)现在是写的MySQL的,如果换别的数据库如redis,pg等是不是就不那么灵活了。 class Database(): def __init__(self, ip, port): self.ip = ip self.port = port @classmethod def tell(cls): obj = cls(IP, PORT) return obj mysql = Database.tell() print(mysql.ip, mysql.port) # 换个IP和端口 IP = '192.168.1.111' PORT = 6379 redis = Database.tell() print(redis.ip, redis.port) # 执行结果: 192.168.1.123 3306 192.168.1.111 6379 # @classmethod 这个装饰器绑定给了类,以后类来调用,会自动把类名当成第一个参数传过来(cls) # 这时调用的时候,使用类名调用 # 如果即用到绑定对象,又用到绑定类,推荐使用绑定对象,因为在对象中可以使用__class__拿到类
5.3 静态方法(非绑定方法)
import random
class Students():
def __init__(self,name,age):
self.name = name
self.age = age
def randnum(self):
print(random.randint(1, 10))
info = Students("Hans", 18)
info.randnum()
info.randnum()
# 执行结果:
1
4
# 在 def randnum(self) 中根本没有用到self,是不是可以不传?
# 现在在类中如果不传就会报错(TypeError: Students.randnum() takes 0 positional arguments but 1 was given),所以可以使用 @staticmethod
import random
class Students():
def __init__(self,name,age):
self.name = name
self.age = age
@staticmethod
def randnum():
print(random.randint(1, 10))
info = Students("Hans", 18)
info.randnum()
info.randnum()
# 执行结果:
4
9
6. 隐藏属性
为什么要隐藏属性?
有些时间有些数据,只想让内部看到,不想让外部看到。
如何隐藏属性
class Students():
shool = '社会大学'
def __init__(self,name,age):
self.name = name
self.age = age
stu1 = Students("Hans", 18)
print(stu1.shool)
#shool是可以拿到的,如何把shool属性隐藏掉
class Students():
__shool = '社会大学' # 只需要在属性前面加两个下划线(__),只在前面加
def __init__(self,name,age):
self.name = name
self.age = age
stu1 = Students("Hans", 18)
print(stu1.shool)
# 执行结果:
AttributeError: 'Students' object has no attribute 'shool'
print(stu1.__shool)
# 执行结果:
AttributeError: 'Students' object has no attribute '__shool'
# 就会发现shool这个不管用什么方法都访问不到了。
# 可以看一下它变成了什么?
print(Students.__dict__)
{'__module__': '__main__', '_Students__shool': '社会大学', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}
# 可以看到shool前面加了双下划线(__)后就变成: _Students__shool(_类名__属性名)
# 现在在外面拿不到了,在内部是否可以拿到?
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.name = name
self.age = age
def get_shool(self):
return self.__shool
stu1 = Students("Hans", 18)
print(stu1.get_shool())
# 执行结果:
社会大学
# 可以看到在内部可以拿到
# 其实如果外部要拿也能拿到:
stu1 = Students("Hans", 18)
print(stu1._Students__shool) # 因为在类的名称空间为_Students__shool,所以直接取这个就可以拿到
print(Students._Students__shool)
# 不但可以隐藏类中的属性也可以隐藏方法和对象中的属性,写法都一样,使用双下划线
# 对象属性:
self.__name
#类中的方法:
def __get_shool(self):
#
# 在外部有方法拿到隐藏的属性,是不是也可以修改? 正常的方法是改不了,可以在类中定义一个专门修改的函数
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.name = name
self.age = age
def get_shool(self):
return self.__shool
def set_shool(self):
self.__shool = "清华大学"
stu1 = Students("Hans", 18)
stu1.set_shool()
print(stu1.get_shool())
# 执行结果:
清华大学
# 更好的方法是给set_shool传值
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.name = name
self.age = age
def get_shool(self):
return self.__shool
def set_shool(self,shoolname):
self.__shool = shoolname
stu1 = Students("Hans", 18)
stu1.set_shool("北京大学")
print(stu1.get_shool())
# 执行结果:
北京大学
# 还可以对修改的内容进行判断,现在要修改的是学校名字,按目前写的传什么值都可以,但有些类型不合适用在这个地方比如int类型,所以可以判断一下。
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.name = name
self.age = age
def get_shool(self):
return self.__shool
def set_shool(self,shoolname):
# if type(shoolname) is not str: return
if not isinstance(shoolname, str): return
self.__shool = shoolname
stu1 = Students("Hans", 18)
stu1.set_shool(123) # 123 不是字符串,直接return返回,所以shool的值还是原来的
print(stu1.get_shool())
# 执行结果:
社会大学
"""
隐藏属性发生了什么?
1, 在类定义阶段发生了变化,如__shool变成了_Students__shool,也只有在定义阶段发生变化,其他地方都不会发生变化
2, 隐藏对外不对内
"""
7. property装饰器
property是python的一种装饰器,是用来修饰方法的。
上面的例子我们把一些不想对外公开的属性隐蔽起来,而只是提供方法给用户操作(查看get_shool和设置set_shool),在方法里面,我们可以检查参数的合理性等。
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.__name = name
self.age = age
def get_name(self):
return self.__name
def set_name(self):
self.__name="ABC"
stu = Students("Hans", 18)
print(stu.get_name())
stu.set_name()
print(stu.get_name())
# 执行结果:
Hans
ABC
# 我们要得到name必须要调用get_name方法,但正常我们取一个属性直接使用.加属性名就可以(stu.name),
# 但现在还要调用一个方法,如何实现即要隐藏属性又要使用.属性获得值,就可以使用@property
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.__name = name
self.age = age
@property
def name(self): # 为了更像stu.name这种方法,把方法名直接改为name
return self.__name
stu = Students("Hans", 18)
print(stu.name)
# 执行结果:
Hans
# @property 装饰器会将 name() 方法转化为一个具有相同名称的只读属性的 "getter"
# 在修改的时候也要像查询一样,直接使得.属性的方式
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.__name = name
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self,name):
if not isinstance(name, str):
print("必须为字符串")
return
self.__name= name
stu = Students("Hans", 18)
print(stu.name) # Hans
stu.name = 123 # 必须为字符串,执行到了isinstance
print(stu.name) # Hans
# 修改为一个合法类型:
stu = Students("Hans", 18)
print(stu.name) # Hans
stu.name = "XYZ"
print(stu.name) # XYZ
# 可以看到已经修改
# 要使用这种方法,在定义时:
"""
@property
def name(self): 1
return self.__name
@name.setter 2
def name(self,xxx) 3
这三个地方的名字一定要一致。
"""
# 删除一个属性
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.__name = name
self.age = age
@property
def name(self):
return self.__name
@name.setter
def name(self,name):
if not isinstance(name, str):
print("必须为字符串")
return
self.__name= name
@name.deleter
def name(self):
del self.__name
print("正在删除%s" % self.__name)
stu = Students("Hans", 18)
del stu.name
print(stu.name)
# 执行结果:
正在删除Hans
# 还有一种写法,现在已经不常用了,即定义一个托管属性
class Students():
__shool = '社会大学'
def __init__(self,name,age):
self.__name = name
self.age = age
def get_name(self):
return self.__name
def set_name(self,name):
if not isinstance(name, str):
print("必须为字符串")
return
self.__name= name
def del_name(self):
print("正在删除%s" % self.__name)
# 定义一个托管属性
name = property(get_name, set_name, del_name)
stu = Students("Hans", 18)
print(stu.name) # 调用get_name 方法
stu.name = "XYZ" # 调用set_name 方法
print(stu.name)
del stu.name # 调用del_name 方法
# 执行结果:
Hans
XYZ
正在删除XYZ
property
就是把方法伪装成属性
property
详细请看官网:
property