面向对象回顾

面向过程与面向对象

面向过程:

核心是过程二字,指的是解决问题的步骤,根据某个业务的流程,以流水线的思维方式编写程序

优点:复杂问题流程化,设计思路清晰

缺点:扩展性差,改变了其中一个环节,就会影响其他环节

面向对象:

对象是特征与技能的封装,利用对象具有的属性和方法来实现对象间的交互。

例子,英雄联盟游戏中,各个英雄具生命值,攻击力等属性;拥有攻击这个技能(在编程时表现为方法)。利用对象的交互来实现游戏的开发。

优点:扩展性强,各个对象具有的属性是相互独立的,改变一个并不会影响其它

缺点:复杂度高于面向过程

类和对象

什么是类:

类是对一系列具有相似属性和技能对象的抽象。

例子,学生都具有姓名,年龄,性别等特征,具有听课,做题等技能。那么把这些相似的特征与技能抽象出来就构成了一个学生类。

为什么要使用类:

# 例子
class Student:
	job = 'student'
	def __init__(self, name, age, gender):
		self.name = name
		self.age = age
		self.gender = gender
	def learn(self):
		print(f'{self.name} is learning')
		
st1 = Student('st1', 18, 'male')
st2 = Student('st2', 19, 'female')
st1.learn()
st2.learn()

# 假如没有类
# 伪代码
st1.name = 'st1'
st1.age = 18
st1.gender = 'male'
st2.name = 'st2'
....

类就是对象的模具,利用模具可以很方便的生产出相似的对象。简单说就是可以减少代码冗余,方便修改。

现实中的模具生产出的对象都是一样的,我们可以利用__init__方法传入参数,产生出既有相同属性却又具有各自属性值的对象。

继承

继承是子类与父类之间的关系(类与对象:实例化)

什么是继承:

子类直接继承父类的属性

为什么要使用继承

# 回到学生类的例子
# 假如还要定义老师类,工作人员类,。。。
# 他们同样都具有name, age, gender的属性
# 可先定义一个People类
class People:
    def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
class Student(People):
	# __int__方法会继承于People类,
	pass
class Teacher(People):
	pass
s = Student('st1', 18, 'male')
t = Teacher('t1', 20, 'female')

父类就是子类的模具,利用模具可以很方便的生产出相似的子类。简单说就是也是可以减少代码冗余,方便修改。

派生

如何在继承父类的同时,又让子类具有自己的属性:使用派生

派生的方法:

  1. 直接定义新的属性
  2. 重用父类方法
# 例子:1. 直接定义新的属性
# 让Student类继承People的同时又具有learn的方法和job属性
# 让Teacher类继承People的同时又具有teach的方法和job属性
class Student(People):
	job = 'student' # 直接定义一个job属性
	def learn(self): # 直接定义一个learn方法
		print(f'{self.name} is learning')
class Teacher(People):
	job = 'teacher'
	def teach(self):
		print(f'{self.name} is teaching')
# 例子:2. 重用父类方法
# 让Teacher继承People类的say_hello方法介绍自己的姓名,同时又介绍自己的角色
class People:
    def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
    def say_hello(self):
        print(f'hello every one!')
        print(f'my name is {self.name}.')
class Teacher(People):
	job = 'teacher'
	def teach(self):
		print(f'{self.name} is teaching')
    def say_hello(self): 
        super().say_hello() # 重用People类的say_hello方法
        print(f'my job is {self.job}') # 加上介绍自己角色的功能
        # People.say_hell(self) # 另外一个调用父类方法的方式,要传入self参数(如果是绑定到对象的方法的话)

组合

组合是对象和对象的关系,具体说是一个对象包含另外一个对象

# 例子学生对象包含一个课程对象
class Course:
    def __init__(self, name, period):
        self.name = name
        self.period = period
python = Course('python', 6)
s = Student('st1', 18, 'male')
s.course = python # 让学生对象包含了一个课程对象
s.cousre.name # 查看课程名称
s.cousre.period # 查看课程周期

小结:继承,派生,实例化,组合

  1. 子类继承父类,类实例化产生对象:父类-->子类-->对象
  2. 子类继承父类时可以通过派生的方法产生自己的属性
  3. 一个对象可以包含其他不同类的对象,如学生对象包含了课程对象,这种思想叫组合

封装

什么是封装

把相关联的数据或方法属性放到一起,并提供有限的访问接口,隐藏某些仅供内部使用的私有属性。

例子:把属性封装到对象中,把一些函数和数据封装到模块中

Python中如何隐藏私有属性

# 在属性名前面加上双下划线 __x, 会自动变形为_类名__x的形式
# 例子(伪代码),利用前面选课系统中的Base类和db_handler
# V0, 不隐藏salary属性
class Teacher(Base):
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary # 不隐藏salary属性
        self.save()
    @classmethod
    def select(cls, name):
        return db_handler.select(cls, name) 
        
t = select('teacher1') 
t.salary = 20000 # 可直接修改

# V1 不允许修改salary,只提供查看的接口,使用__salary隐藏属性
class Teacher(Base):
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary # 隐藏salary属性
        self.save()
    def get_salary(self): # 提供一个查看salary的接口
        return self.__salary 
    @classmethod
    def select(cls, name):
        return db_handler.select(cls, name)
        
t = select('teacher1') 
# t.salary = 20000 # 现在不能在外部使用这种方法改变salary
print(t.get_salary()) # 只提供了一个查看的salary的接口

# 进阶补充 如何使得查看salary不使用一个方法t.get_salary()来查看,而使用t.salary的方法查看:使用property装饰器
# V2 使用property装饰器,隐藏salary属性的同时,提供t.salary的方式查看
class Teacher(Base):
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary # 隐藏salary属性
        self.save()
    @property
    def salary(self): # 提供一个查看salary的接口
        return self.__salary 
    @classmethod
    def select(cls, name):
        return db_handler.select(cls, name)
t = select('teacher1') 
# t.salary = 20000 # 依然不能在外部使用这种方法改变salary
print(t.salary) # 但可以用t.salary查看,提供了一个更加自然的访问私有属性的方法,对比t.get_salary()方法


小结:封装,Python访问控制机制,property装饰器

  1. 封装只是一个思想,比如类对属性的封装,模块对数据和函数封装。
  2. 封装可以理解为两层含义:a. 把东西封装起来,b. 控制私有属性的访问和修改,同时提供必要的接口
  3. Python中可以使用 __属性 的方式隐藏属性,但在python中并不是强制性的。依然可以通过 对象._类名__属性名 访问或修改
  4. 在python中可以用property这个工具,使得可以隐藏私有属性的同时,提供一个优雅的查询方式(对比:t.get_salary() 和t.salary)

多态性

什么是多态性

# 例子, 伪代码
cat.speak() # miao miao ~
dog.speak() # wang wang ~
cow.speak() # mou mou ~

用同一个方法名称调用,却又有不同的效果,表现为多种不同的形态

如何实现多态

# 1. 使用继承抽象类
import abc
from abc import abstractmethod

class Animal(metaclass=abc.ABCMeta):
	@abstractmethod
	def speak(self):
		pass
class Cat(Animal):
    def speak(self): # 必须定义speak, 否则实例化时会报错
        print('miao miao ~') 
class Dog(Animal):
    def speak(self): # 必须定义speak, 否则实例化时会报错
        print('wang wang ~') 
c = Cat()
d = Dog()
c.speak()
d.speak()
		
# 2. 使用鸭子类型
class Cat: # 不再继承于某个特定的类
    def speak(self): # 用一个统一的函数名来表示动物叫这个方法
        print('miao miao ~') 
class Dog:
    def speak(self): # 用一个统一的函数名来表示动物叫这个方法
        print('wang wang ~') 
c = Cat()
d = Dog()
c.speak()
d.speak()

什么是鸭子类型:不是一个具体的类,如果某些对象都具有统一的调用方法,我们可以说他们属于同一个鸭子类型。在上个例子中我们可以说Cat和Dog都属于 Animal这样一个鸭子类型(但我们并没有定义Animal, 也没有使用继承,只是我们编写程序时让他们具有了统一的speak方法,看起来像属于同一个类型)

小结:多态性,抽象类和鸭子类型

  1. 多态性也只是面向对象编程中的一种思想,简单的说就是不同对象有相同的调用方法,却会产生不同的调用结果
  2. 在python中可以用抽象类和鸭子类型实现多态性
  3. 在python中推荐使用鸭子类型,而不推荐使用抽象类。
原文地址:https://www.cnblogs.com/1012zlb/p/12063332.html