Day6 - Python基础6 面向对象编程

时间:2022-12-20 14:54:02

Day6 - Python基础6 面向对象编程

面向过程介绍

  1.核心是过程,过程指的是解决问题的步骤,先干什么再干什么,好比精心设计一条流水线是一种机械式的思维方式

  2.优点:复杂的问题的流程化、简单化

  3.缺点:一套程序就是用来解决一个问题,适合简单的场景

  4.应用场景:一旦完成基本很少变更的场景,如Linux内核

面向对象介绍

  1.核心是对象,对象是特征和技能的结合体,面向对象编程就好比创造一个世界,你自己就是这个世界的上帝,存在皆为对象,不存在也可创造出来,面向对象更注重对现实世界的模拟,是一种“上帝式”的思维方式

  2.优点:解决了程序的可扩展性,对某一个对象的单独修改,会立刻反映到全体系

  3.缺点:1.编程的复杂度高于面向过程;2.不能象面向过程能精准预测处理流程和结果

  4.应用场景:需求经常变化的软件

类与对象

  类是类别和种类,是面向对象最重要的概念,对象是特征和技能的结合体,类是一系列有相似特征和技能对象的结合体

  现实世界:先有对象再有类  在程序中:先定义类,再产生对象

  在程序中特征用变量表识,技能用函数表示;类中最常见的是变量和函数的定义

  世界万物,皆可分类;只要是对象,就肯定属于某种品类;只要是对象,就肯定有属性

  类的2种属性:1.数据属性是所有对象共享的;2.函数属性是绑定给对象用的

  

构造函数

   __init__方法

    1.该方法内可以有任意python代码;2.一定不能有返回值

析构函数:在实例释放、销毁的时候执行的,通常用于做一些收尾工作,如关闭一些数据库链接和临时文件

 

私有方法,私有属性:只有实例内部能访问,通过设置私有方法可以访问

 

封装

 

继承

 Day6 - Python基础6 面向对象编程

继承是创建新类的一种方式,新建的类可以继承一个或多个类,父类称为基类或超类,新建类称为派生类或子类。子类继承父类的属性,从而解决代码重用的问题。

Python3.x版本默认都是新式类

 继承与抽象:父子类的关系是怎么产生的呢,先抽象再继承,抽取比较像或类似的地方,抽象的主要作用是划分类别,隔离关注点,降低复杂度。继承是抽象的结果,先抽象再继承,才能通过继承的方式表达抽象的结构。

 1 >>> class Foo:
 2 ...     def f1(self):
 3 ...         print('Foo.f1')
 4 ...     def f2(self):
 5 ...         print('Foo.f2')
 6 ...         self.f1()
 7 ...
 8 >>> class Bar(Foo):
 9 ...     def f1(self):
10 ...         print('Bar.f1')
11 ...
12 >>> b=Bar()
13 >>> b.f2()  #Bar中没有f2方法,就云父类Foo中查找,执行时因Bar中定义了f1,所以优先执行Bar中的f1
14 Foo.f2  
15 Bar.f1

组合与重用性

指的是,在一个类中以另外一个类的对象为数据属性,称为类的组合。

 1 >>> class Equip: #武器装备类
 2 ...     def fire(self):
 3 ...         print('release Fire skill')
 4 ... 
 5 >>> class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
 6 ...     camp='Noxus'
 7 ...     def __init__(self,nickname):
 8 ...         self.nickname=nickname
 9 ...         self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性
10 ... 
11 >>> r1=Riven('锐雯雯')
12 >>> r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
13 release Fire skill

继承和组合是有效利用现有类的重要方式,只是概念和应用场景不同

1.继承:通过继承建立了类和派生类的关系,它是一种“是”的关系,如人是动物,当类中有很多相同的功能 ,可以把这种相似的共同功能提取为基类,这种情况下用继承比较好

2.组合:用组合建立了类和组合的关系,它是一种“有”的关系,当类之间有很大不同,且较小类是较大类所需要的组件时,用组合比较好

Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
 1 class People:
 2     def __init__(self,name,age,sex):
 3         self.name=name
 4         self.age=age
 5         self.sex=sex
 6 
 7 class Course:
 8     def __init__(self,name,period,price):
 9         self.name=name
10         self.period=period
11         self.price=price
12     def tell_info(self):
13         print('<%s %s %s>' %(self.name,self.period,self.price))
14 
15 class Teacher(People):
16     def __init__(self,name,age,sex,job_title):
17         People.__init__(self,name,age,sex)
18         self.job_title=job_title
19         self.course=[]
20         self.students=[]
21 
22 
23 class Student(People):
24     def __init__(self,name,age,sex):
25         People.__init__(self,name,age,sex)
26         self.course=[]
27 
28 
29 egon=Teacher('egon',18,'male','沙河霸道金牌讲师')
30 s1=Student('牛榴弹',18,'female')
31 
32 python=Course('python','3mons',3000.0)
33 linux=Course('python','3mons',3000.0)
34 
35 #为老师egon和学生s1添加课程
36 egon.course.append(python)
37 egon.course.append(linux)
38 s1.course.append(python)
39 
40 #为老师egon添加学生s1
41 egon.students.append(s1)
42 
43 
44 #使用
45 for obj in egon.course:
46     obj.tell_info()
继承和组合例子

接口和归一化

接口提取了一群类共同的函数,可以当做是一个函数的集合,然后让子类云实现接口的函数,这叫归一化,什么是归一化,就是基于同一接口实现的类 ,所有这些类产生的对象都有共同的使用方法

好处:1.使用者无需关系对象的类是什么,只需要知道这此对象具备某些功能就可以了,降低使用难度

   2.使用者可以不加区分处理所有接口兼容的对象集合

实现方法:python没有interface的关键字

                 1.可以借助第三方模块;

                 2.使用继承

2.1.继承基类的方法,并且做出自己的改变或者扩展(代码重用):实践中,继承的这种用途意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。

2.2.声明某个子类兼容于某基类,定义一个接口类(模仿java的Interface),接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

抽象类

抽象类是一个介于类和接口之间的概念,同时具备类和接口的部分特性,可以用做归一化设计

1.抽象类也要基于模块实现,只是被继承,不能实例化,如水果这个类,你不能实例化

2.为什么要有抽象类

  如果说类是从一堆对象中抽取相同的内容而来,那么抽象类就从一堆类中抽取的相同内容而来,这一点和接口有点相似,但又有不同

子类调用父类的方法

二种方法都可以,最好不要混用

方法一:指名道姓,父类名.父类方法()

 1 class Vehicle: #定义交通工具类
 2      Country='China'
 3      def __init__(self,name,speed,load,power):
 4          self.name=name
 5          self.speed=speed
 6          self.load=load
 7          self.power=power
 8 
 9      def run(self):
10          print('开动啦...')
11 
12 class Subway(Vehicle): #地铁
13     def __init__(self,name,speed,load,power,line):
14         Vehicle.__init__(self,name,speed,load,power)
15         self.line=line
16 
17     def run(self):
18         print('地铁%s号线欢迎您' %self.line)
19         Vehicle.run(self)
20 
21 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
22 line13.run()

方法二:super

 1 class Vehicle: #定义交通工具类
 2      Country='China'
 3      def __init__(self,name,speed,load,power):
 4          self.name=name
 5          self.speed=speed
 6          self.load=load
 7          self.power=power
 8 
 9      def run(self):
10          print('开动啦...')
11 
12 class Subway(Vehicle): #地铁
13     def __init__(self,name,speed,load,power,line):
14         #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
15         super().__init__(name,speed,load,power)
16         self.line=line
17 
18     def run(self):
19         print('地铁%s号线欢迎您' %self.line)
20         super(Subway,self).run()
21 
22 class Mobike(Vehicle):#摩拜单车
23     pass
24 
25 line13=Subway('中国地铁','180m/s','1000人/箱','',13)
26 line13.run()
Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
 1 #A没有继承B,但是A内super会基于C.mro()继续往后找
 2 class A:
 3     def test(self):
 4         super().test()
 5 class B:
 6     def test(self):
 7         print('from B')
 8 class C(A,B):
 9     pass
10 
11 c=C()
12 c.test() #打印结果:from B
13 
14 
15 print(C.mro())
16 #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
即使没有直接继承关系,super仍然会按照mro继续往后查找

 多态

   1.一种事物有多种形态,如动物形态有:人、猪、狗

   2.多态性:指不考虑实例的类型的情况下使用实例

   面向对象中是这样表述多态性的:向不同的对象发送同样的消息(obj.func(),调用obj的func方法,又称为向obj发送一条消息func),不同对象在接收时产生不同的行为,每个对象可以用自己的方式去响应共同的消息。消息就是指调用函数,不同的行为就是不同的实现,即执行不同的函数 。

如下课铃响了,老师是下班,同学是下课。

  多态性分为静态多态性和动态多态性

  静态多态性:如任何类型都可运算符+运算

  动态多态性;

Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
 1 class Animal:
 2     def __init__(self,name):
 3         self.name = name
 4     def talk(self):
 5         pass
 6 
 7     @staticmethod
 8     def func(obj):
 9         obj.talk()
10 
11 class Person(Animal):
12     def talk(self):
13         print('%s:Hello...'%self.name)
14 
15 class Cat(Animal):
16     def talk(self):
17         print('%s:Miao....'%self.name)
18 
19 class Dog(Animal):
20     def talk(self):
21         print('%s:Wa....'%self.name)
22 
23 person1 = Person("")
24 cat1 = Cat("")
25 dog1 = Dog("")
26 
27 # person1.talk()
28 # cat1.talk()
29 # dog1.talk()
30 
31 Animal.func(person1)
32 Animal.func(cat1)
33 Animal.func(dog1)
动态多态性实例

  3.多态性的好处

   a.增加程序的灵活性,不变应万变,不论对象千变万化,用户可以用同样的方法调用

   b.增加程序的可扩展性

   python默认支持多态

 1 #Python的序列类型有多种形态:字符串,列表,元组,多态性体现如下
 2 #str,list,tuple都是序列类型
 3 s=str('hello')
 4 l=list([1,2,3])
 5 t=tuple((4,5,6))
 6 
 7 #我们可以在不考虑三者类型的前提下使用s,l,t
 8 s.__len__()
 9 l.__len__()
10 t.__len__()
11 
12 len(s)
13 len(l)
14 len(t)  #可以使用同样的方法调用 

 封装

 1.封装=隐藏,这种理解是片面的

 2.如何隐藏

  在类中使用双下划线开头的方式将属性隐藏(设置私有的),

    注意事项: 

      1.这种不是严格意义上的限制外部访问,只是语法上的变形,仅限制外部直接访问

    2.变形只在类的定义发生一次,后面的赋值不会变形

              Day6 - Python基础6 面向对象编程

   3.在继承中,父类如不想让子类覆盖自己的方法,可以将方法定义为私有的

 1 #其实只是一种变形操作,只在类定义时发生
 2 #"__x"在类定义时都自动变成"_类名__x"
 3 class A:
 4     __N=0                    #变形为_A__N
 5     def __init__(self):
 6         self.__X=10        #变形为self._A__X
 7     def __foo(self):       #变形为_A__foo
 8         print('from A')
 9     def bar(self):
10         self.__foo()          #只有类内部可以用__foo方式访问
11 
12 # print(A.__dict__)
13 a = A()
14 print(a._A__N)    #在外面用变形后的方式可以访问到
15 a._A__foo()
16 a.bar()

 封装不是单纯意义上的隐藏

 封装的真谛是严格区分内外部访问,内部可以直接访问,外部不能直接访问。然而定义属性的目的终归是要用,外部要想用类的隐藏属性,需要我们开辟接口,让外部能间接的访问到隐藏的属性

 1.封装数据:将数据隐藏不是目的,隐藏属性然后提供外部访问接口,我们可以在接口上附加上对该数据操作的限制,以此完成对数据属性操作的严格控制

Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
 1 class Teacher:
 2     def __init__(self,name,age):
 3         # self.__name=name
 4         # self.__age=age
 5         self.set_info(name,age)
 6     def tell_info(self):
 7         print('name:%s,age:%s'%(self.__name,self.__age))
 8     def set_info(self,name,age):
 9         if not isinstance(name,str):
10             raise  TypeError('name必须是string')
11         if not isinstance(age,int):
12             raise TypeError('age必须是整型')
13         self.__name=name
14         self.__age=age
15 
16 t=Teacher(1,18)
17 t.tell_info()
18 t.set_info('jack',18)
19 t.tell_info()
封装数据限制访问实例

   2.封装的目的:是为了隔离复杂度

封装方法举例:

1. 电视机本身是一个黑盒子,隐藏了所有细节,但是一定会对外提供了一堆按钮,这些按钮也正是接口的概念,所以说,封装并不是单纯意义的隐藏!!!

2. 快门就是傻瓜相机为傻瓜们提供的方法,该方法将内部复杂的照相功能都隐藏起来了

提示:在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数,这与接口的概念还不一样,接口代表一组接口函数的集合体。

特性@property

  使用property的原因:绑定属性时,如果我们直接把参数暴露出去,写起来简单,但没法检查参数的合法性

  1.property是一种属性,把一个方法变成属性,访问它时会先执行一段功能(函数)然后返回值

Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
 1 import math
 2 class Circle:
 3     def __init__(self,radius): #圆的半径radius
 4         self.radius=radius
 5 
 6     @property
 7     def area(self):
 8         return math.pi * self.radius**2 #计算面积
 9 
10     @property
11     def perimeter(self):
12         return 2*math.pi*self.radius #计算周长
13 
14 c=Circle(10)
15 print(c.radius)
16 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
17 print(c.perimeter) #同上
18 '''
19 输出结果:
20 314.1592653589793
21 62.83185307179586
22 '''
例:圆的周长和面积

  2.将一个类的函数定义成特性后,对象再去使用函数时,根本不会觉得是在执行一个函数计算出来的,这遵循了统一访问的原则

ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开

封装与扩展性

  封装可以区分内外部,所以类实现者可以修改内部代码而不影响外部调用者的代码 ,外部用户只知道一个接口,只要接口不变,使用者的代码永远不需改变,这提供一个良好的合作基础。

静态方法和类方法

  有效的进行了代码隔离,有利于组织代码,有利于命名空间的整洁。

  静态方法@staicmethod:不用访问类变量和实例变量,也不能接收self参数传值。但对象和类可以访问它

  用处:可以用来组织类之间有逻辑关系的函数。在很多情况下,有些函数和类相关,但又不需要任何类和实例变量就可以实现一此功能 。如环境变量的设置,修改另一个类的属性,假如我们只是想实现类之间的交互而不是通过实例,我们可以在类之外创建一个函数实现这些功能,但这会使代码扩散到类外,不利于代码的维护。

  类方法@classmethod:只能访问类变量,不能访问实例变量

  用处:尤其适合在创建类实例之前做些预设置,实例创建前不能使用实例方法,只能使用classmethod.另一个好处是,以后重构类时不需要修改构造函数,只需要添加要处理的函数,然后加classmethod即可,相当于我们拥有了多样化的构造函数。 

Day6 - Python基础6 面向对象编程Day6 - Python基础6 面向对象编程
class Dog(object):
    name = '我是類變量'
    def __init__(self, name):
        self.name = name

    @classmethod
    def eat(self):
        print("%s is eating" % self.name)

d1 =Dog('jack')
d1.eat()    
Dog.eat()

#结果
我是類變量 is eating
我是類變量 is eating
类方法实例