Python|Python 面向对象程序设计

Python 面向对象程序设计
目录

  • Python 面向对象程序设计
    • 1 面向过程编程
    • 2 面向对象编程
    • 3. 面向过程和面向对象的优缺点
    • 4. 由浅入深了解面向对象
      • 4.1 学生选课为例
      • 4.2 类和对象
      • 4.3 属性的查找顺序
    • 5. 面向对象绑定方法
      • 5.1 小案例:计算一共产生了多少个对象
      • 5.2 绑定方法
      • 5.3 静态方法(非绑定方法)
    • 6. 隐藏属性
    • 7. property装饰器
    • 未完待续

Python|Python 面向对象程序设计
文章图片

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 = courseinit('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 = coursestu1 = 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 = courseshool = '社会大学'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 = courseshool = '社会大学'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 = courseshool = '社会大学' 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 = courseshool = '社会大学' 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 = courseshool = '社会大学'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 = courseshool = '社会大学'print(stu1.shool)# 这个shool为对象中定义的 清华大学

5. 面向对象绑定方法
5.1 小案例:计算一共产生了多少个对象
class Count(): count = 0def __init__(self, name): self.name = name Count.count +=1# 统计次数s1 = Count('Hello') s2 = Count('World') print(Count.count) # 执行结果: 2# 类属性修改,所有对象都改。# 还有一种写法: class Count(): count = 0def __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 = genderdef 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 = 3306class Mysql(): def __init__(self, ip, port): self.ip = ip self.port = portmysql = Mysql(IP, PORT) print(mysql.ip,mysql.port) # 执行结果: 192.168.1.123 3306# 如果有多个需要写多次。能不能把Mysql(IP, PORT)这个只写一次 # 可以把这个放到一个函数里IP = '192.168.1.123' PORT = 3306class Mysql(): def __init__(self, ip, port): self.ip = ip self.port = portdef tell(self): mysql = Mysql(IP, PORT) return mysqlobj= 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 objmysql= 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),所以可以使用 @staticmethodimport 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 = agestu1 = Students("Hans", 18) print(stu1.shool) #shool是可以拿到的,如何把shool属性隐藏掉class Students(): __shool = '社会大学'# 只需要在属性前面加两个下划线(__),只在前面加 def __init__(self,name,age): self.name = name self.age = agestu1 = 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.__shoolstu1 = 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 = shoolnamestu1 = 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 = shoolnamestu1 = Students("Hans", 18) stu1.set_shool(123)# 123不是字符串,直接return返回,所以shool的值还是原来的 print(stu1.get_shool()) # 执行结果: 社会大学""" 隐藏属性发生了什么? 1, 在类定义阶段发生了变化,如__shool变成了_Students__shool,也只有在定义阶段发生变化,其他地方都不会发生变化 2, 隐藏对外不对内 """

Python|Python 面向对象程序设计
文章图片

7. property装饰器
property是python的一种装饰器,是用来修饰方法的。
上面的例子我们把一些不想对外公开的属性隐蔽起来,而只是提供方法给用户操作(查看get_shool和设置set_shool),在方法里面,我们可以检查参数的合理性等。
class Students(): __shool = '社会大学' def __init__(self,name,age): self.__name = name self.age = agedef get_name(self): returnself.__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 returnself.__namestu = 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): returnself.__name@name.setter def name(self,name): if not isinstance(name, str): print("必须为字符串") return self.__name= namestu = 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 returnself.__name@name.setter2 def name(self,xxx) 3 这三个地方的名字一定要一致。 """# 删除一个属性 class Students(): __shool = '社会大学' def __init__(self,name,age): self.__name = name self.age = age@property def name(self): returnself.__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 = agedef get_name(self): returnself.__namedef set_name(self,name): if not isinstance(name, str): print("必须为字符串") return self.__name= namedef 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详细请看官网:
【Python|Python 面向对象程序设计】property
未完待续

    推荐阅读