• Day6


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

    面向过程介绍

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

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

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

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

    面向对象介绍

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

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

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

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

    类与对象

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

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

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

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

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

      

    构造函数

       __init__方法

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

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

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

    封装

    继承

     

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

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

     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()
     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),不同对象在接收时产生不同的行为,每个对象可以用自己的方式去响应共同的消息。消息就是指调用函数,不同的行为就是不同的实现,即执行不同的函数 。

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

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

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

      动态多态性;

     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.变形只在类的定义发生一次,后面的赋值不会变形

                  

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

     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是一种属性,把一个方法变成属性,访问它时会先执行一段功能(函数)然后返回值

     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即可,相当于我们拥有了多样化的构造函数。 

    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
    类方法实例
  • 相关阅读:
    第十三周助教小结
    记事本
    第十二周助教小结
    与周老师会谈之后的感想
    第十一周总结
    第十周助教总结
    听周筠老师一席话,受益匪浅
    2020软件工程作业04
    2020软件工程作业02
    2020软件工程作业01
  • 原文地址:https://www.cnblogs.com/pynetwork/p/9012579.html
Copyright © 2020-2023  润新知