zoukankan      html  css  js  c++  java
  • python之递归函数、二分查找、面向对象、封装(6)

    本节内容:递归函数、二分查找、面向对象、什么是类、什么是对象、组合、面向对象的三大特征

      1.递归函数

      2.二分查找

      3.面向对象

         3.1.什么是类

         3.2.什么是对象

          3.3.类名称空间、对象名称空间

      4.组合

      5.面向对象的三大特征

    1、递归函数                                                                                                 

    递归函数:在一个函数里在调用这个函数本身。

    递归的最大深度:998

    正如你们刚刚看到的,递归函数如果不受到外力的阻止会一直执行下去。但是我们之前已经说过关于函数调用的问题,每一次函

    数调用都会产生一个属于它自己的名称空间,如果一直调用下去,就会造成名称空间占用太多内存的问题,于是python为了杜绝

    此类现象,强制的将递归层数控制在了997(只要997!你买不了吃亏,买不了上当...).

    拿什么来证明这个“998理论”呢?这里我们可以做一个实验:

    def foo(n):
        print(n)
        n += 1
        foo(n)
    foo(1)
    View Code

    由此我们可以看出,未报错之前能看到的最大数字就是998.当然了,997是python为了我们程序的内存优化所设定的一个默认值,

    我们当然还可以通过一些手段去修改它: 

    import sys
    sys.setrecursionlimit(100000)
    count = 0
    def func1():
        global count
        count += 1
        print(count)
        func1()
    func1()
    View Code

    我们可以通过这种方式来修改递归的最大深度,刚刚我们将python允许的递归深度设置为了10w,至于实际可以达到的深度就取

    决于计算机的性能了。不过我们还是不推荐修改这个默认的递归深度,因为如果用997层递归都没有解决的问题要么是不适合使

    用递归来解决要么是你代码写的太烂了~~~

    1.1 递归函数的示例                    

    这里我们又要举个例子来说明递归能做的事情。

    例一:

    现在你们问我,alex老师多大了?我说我不告诉你,但alex比 egon 大两岁。

    你想知道alex多大,你是不是还得去问egon?egon说,我也不告诉你,但我比武sir大两岁。

    你又问武sir,武sir也不告诉你,他说他比太白大两岁。

    那你问太白,太白告诉你,他23了。

    这个时候你是不是就知道了?alex多大?

    首先,你是不是问alex的年龄,结果又找到egon、武sir、太白,你挨个儿问过去,一直到

    拿到一个确切的答案,然后顺着这条线再找回来,才得到最终alex的年龄。这个过程已经

    非常接近递归的思想。我们就来具体的我分析一下,这几个人之间的规律。你为什么能知道的?

    """
    alex 他比佩奇 大两岁。  4   age(3) + 2
    佩奇 他比日天 大两岁。  3   age(2) + 2
    日天 他比太白 大两岁。  2   age(1)  + 2
    太白:我今年23.         1   23
    """
    def age(n):
        if n == 1:
            return 23
        else:
            return age(n-1) + 2
    
    print(age(4))
    """
    def age(4):
        if n == 1:
            return 23
        else:
            return age(3) + 2   23 + 2 + 2 + 2
    
    def age(3):
        if n == 1:
            return 23
        else:
            return age(2) + 2   23 + 2 + 2
             
    def age(2):
        if n == 1:
            return 23
        else:
            return age(1) + 2    23 + 2
            
    def age(1):
        if n == 1:
            return 23
        else:
            return age(0) + 2
    
    """
    
    def age(n):
        if n == 1:
            return 23
        else:
            age(n-1) + 2
    """
    def age(2):
        if n == 1:
            return 23
        else:
            age(1) + 2   23 + 2 
    
    def age(1):
        if n == 1:
            return 23
        else:
            age(1) + 2
    """
    print(age(2))
    View Code

    2、二分查找                                                                                                   

    如果有这样一个列表,让你从这个列表中找到66的位置,你要怎么做?

    l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

    你说,so easy!

    l.index(66)...

    我们之所以用index方法可以找到,是因为python帮我们实现了查找方法。如果,index方法不给你

    用了。。。你还能找到这个66么?

    l=[2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]
    
    i = 0
    for num in l:
        if num == 66:
            print(i)
        i+=1
    View Code

    上面这个方法就实现了从一个列表中找到66所在的位置了。

    2.1 二分查找示例                      

    l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]

    你观察这个列表,这是不是一个从小到大排序的有序列表呀?

    如果这样,假如我要找的数比列表中间的数还大,是不是我直接在列表的后半边找就行了?

    简单版二分查找

    l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]
    
    def func(l,aim):
        mid = (len(l)-1)//2
        if l:
            if aim > l[mid]:
                func(l[mid+1:],aim)
            elif aim < l[mid]:
                func(l[:mid],aim)
            elif aim == l[mid]:
                print("bingo",mid)
        else:
            print('找不到')
    func(l,66)
    func(l,6)
    View Code

    升级版二分查找

    l1 = [1, 2, 4, 5, 7, 9]
    def two_search(l,aim,start=0,end=None):
        end = len(l)-1 if end is None else end
        mid_index = (end - start) // 2 + start
        if end >= start:
            if aim > l[mid_index]:
                return two_search(l,aim,start=mid_index+1,end=end)
    
            elif aim < l[mid_index]:
                return two_search(l,aim,start=start,end=mid_index-1)
    
            elif aim == l[mid_index]:
                return mid_index
            else:
                return '没有此值'
        else:
            return '没有此值'
    print(two_search(l1,9))
    View Code

    3、面向对象                                                                                                

    实际工作中,python 都是面向对象,写代码,或者 面向对象+函数写代码。

    面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,

    面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

    优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

    缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。

    应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以

    及Apache HTTP Server等。

    面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向

    对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

    了解一些名词:类、对象、实例、实例化

    类:具有相同特征的一类事物(人、狗、老虎)

    对象/实例:具体的某一个事物(隔壁阿花、楼下旺财)

    实例化:类——>对象的过程

    3.1 什么是类                          

    声明

    def functionName(args):
         '函数文档字符串'
          函数体 
    
    
    '''
    class 类名:
        '类的文档字符串'
        类体
    '''
    
    #我们创建一个类
    class Data:
        pass
    
    声明函数vs声明类
    View Code

    属性

    class Person:   #定义一个人类
        role = 'person'  #人的角色属性都是人
        def walk(self):  #人都可以走路,也就是有一个走路方法
            print("person is walking...")
    
    
    print(Person.role)  #查看人的role属性
    print(Person.walk)  #引用人的走路方法,注意,这里不是在调用
    View Code

    实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

    class Person:   #定义一个人类
        role = 'person'  #人的角色属性都是人
        def __init__(self,name):
            self.name = name  # 每一个角色都有自己的昵称;
            
        def walk(self):  #人都可以走路,也就是有一个走路方法
            print("person is walking...")
    
    
    print(Person.role)  #查看人的role属性
    print(Person.walk)  #引用人的走路方法,注意,这里不是在调用
    View Code

    实例化的过程就是类——>对象的过程

    原本我们只有一个Person类,在这个过程中,产生了一个egg对象,有自己具体的名字、攻击力和生命值。

    语法:对象名 = 类名(参数)

    class Person:  # class 关键字,定义了一个类
        '''
        类里面的所有内容
        '''
        animal = '高级动物' # 静态变量
        soup = '有思想'  # 静态变量
    
        def __init__(self,name,sex,eye,high,weight,):  # 构造方法
    
            self.eye = eye  # 属性
            self.name = name
            self.sex = sex
            self.high = high
            self.weight = weight
            print(666)
    
        def work(self): # 动态变量,动态方法,方法
            print(self)
            # self.job = 'IT'
            print('人会工作....')
    类的示例
    类如何调用查看静态变量,动态变量
    类操作静态变量有两种方式:
    1,类名.__dict__方法  只能查看,不能增删改。
    # print(Person.__dict__)
    # print(Person.__dict__['animal'])
    # Person.__dict__['name'] = 'alex'
    # Person.__dict__['animal'] = '低级动物'
     2,类名.变量名  可增删改查
    # print(Person.animal)
    # print(Person.soup)
    # Person.kind = '有性格'
    # Person.animal = '低等动物'
    # del Person.kind
    # print(Person.__dict__)
    
    # 一般你想查询全部的静态变量时,用__dict__ 其他全部都用类名.变量名。
    
     类操作方法有两种方式:
     1,类名.__dict__[方法名]()
    # print(Person.__dict__['work'](11))
      2,类名.方法名
    # Person.work(11)
    # 如果类操作方法:类名.方法名()
    # 只要创建一个类,里面的内容就已经加载到内存。
    # print(Person.__dict__)
    View Code

    self

    self:在实例化时自动将对象/实例本身传给__init__的第一个参数,你也可以给他起个别的名字,

    但是正常人都不会这么做。因为你瞎改别人就不认识

    一:我们定义的类的属性到底存到哪里了?有两种方式查看
    dir(类名):查出的是一个名字列表
    类名.__dict__:查出的是一个字典,key为属性名,value为属性值
    
    二:特殊的类属性
    类名.__name__# 类的名字(字符串)
    类名.__doc__# 类的文档字符串
    类名.__base__# 类的第一个父类(在讲继承时会讲)
    类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
    类名.__dict__# 类的字典属性
    类名.__module__# 类定义所在的模块
    类名.__class__# 实例对应的类(仅新式类中)
    类属性的补充

    3.2 什么是对象                           

    对象: 类名() 实例化一个对象

    只要实例化一个对象,自动触发__init___

    内部进行三步:

    1,实例化一个对象,在内存中产生一个对象空间。

    2,自动执行init方法,并将这个空间对象。 <__main__.Person object at 0x0000000001F5ABE0> 传给self

    3,通过构造方法里的代码给空间对象添加一些属性,并返回给对象。

    对象如何调用查看静态变量,动态变量

    对象操作属性变量有两种方式:
    1,对象.__dict__方法  只能查看,不能增删改。
    # print(p1.__dict__)
    2,对象.变量名  可增删改查
    # print(p1.name)
    # print(p1.eye)
    # p1.color = '黄皮肤'
    # print(p1.color)
    # print(p1, type(p1))
    3,可以访问类的静态变量
    # print(p1.__dict__)
    # print(p1.animal)
    # print(p1.soup)
    # 一般你想查询全部的静态变量时,用__dict__ 其他全部都用类名.变量名。
    
    # 对象操作方法有两种方式:
    1,对象.方法名()
    # p1.work()
    # print(p1)
    # print(p1.__dict__)
    2,类名.方法名(对象)
    # Person.work(111)
    # Person.work(p1)
    View Code

    对象的相关知识

    class 类名:
        def __init__(self,参数1,参数2):
            self.对象的属性1 = 参数1
            self.对象的属性2 = 参数2
    
        def 方法名(self):pass
    
        def 方法名2(self):pass
    
    对象名 = 类名(1,2)  #对象就是实例,代表一个具体的东西
                      #类名() : 类名+括号就是实例化一个类,相当于调用了__init__方法
                      #括号里传参数,参数不需要传self,其他与init中的形参一一对应
                      #结果返回一个对象
    对象名.对象的属性1   #查看对象的属性,直接用 对象名.属性名 即可
    对象名.方法名()     #调用类中的方法,直接用 对象名.方法名() 即可
    View Code
    '''
    练习一:在终端输出如下信息
     
    小明,10岁,男,上山去砍柴 
    小明,10岁,男,开车去东北
    小明,10岁,男,最爱大保健
    老李,90岁,男,上山去砍柴
    老李,90岁,男,开车去东北
    老李,90岁,男,最爱大保健
    
    '''
    def chop_wood(name,age,sex):
        print('%s,%s岁,%s,上山去砍柴'%(name,age,sex))
    
    def driver(name,age,sex):
        print('%s,%s岁,%s,开车去东北'%(name,age,sex))
    
    def healthcare(name,age,sex):
        print('%s,%s岁,%s,最爱大保健'%(name,age,sex))
    
    chop_wood('小明',12,'')
    driver('小明',12,'')
    healthcare('小明',12,'')
    chop_wood('老李',22,'')
    
    =================
    class Dayaction:
        def __init__(self,name,age,sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def chop_wood(self):
            print('%s,%s岁,%s,上山去砍柴'%(self.name,self.age,self.sex))
    
        def driver(self):
            print('%s,%s岁,%s,开车去东北'%(self.name,self.age,self.sex))
    
        def healthcare(self):
            print('%s,%s岁,%s,最爱大保健'%(self.name,self.age,self.sex))
    
    p1 = Dayaction('小明', 15, '')
    p1.chop_wood()
    p1.driver()
    p1.healthcare()
    练习

    3.3 类名称空间、对象名称空间              

    创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

    而类有两种属性:静态属性和动态属性

    • 静态属性就是直接在类中定义的变量
    • 动态属性就是定义在类中的方法

    其中类的数据属性是共享给所有对象的

    >>>id(egg.role)
    4341594072
    >>>id(Person.role)
    4341594072
    View Code

    而类的动态属性是绑定到所有对象的

    >>>egg.attack
    <bound method Person.attack of <__main__.Person object at 0x101285860>>
    >>>Person.attack
    <function Person.attack at 0x10127abf8> 
    View Code

    在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...

    最后都找不到就抛出异常

    4、组合                                                                                                         

    组合:给一个类对象的属性 封装 另一个类的对象。

    class Game_person:
        def __init__(self,nickname,sex,hp,ad):
            self.nickname = nickname
            self.sex = sex
            self.hp = hp
            self.ad = ad
        def attack(self,p):
            p.hp -= self.ad
            print('%s攻击了%s,%s还剩%s血量'%(self.nickname,p.nickname,p.nickname,p.hp))
    
        def weapon_attack(self,武器):
            self.武器 = 武器 #斧子对象
    
    class Weapon:
        def __init__(self,name,ad):
            self.name=name
            self.ad=ad
    
        def fight(self,p1,p2):
            p2.hp -= self.ad
            print('%s使用%s打了%s%s血,%s还剩%s滴血'
                  %(p1.nickname,self.name,p2.nickname,self.ad,p2.nickname,p2.hp))
    
    ts = Game_person('泰森','',200,50)
    barry = Game_person('太白','',100,10)
    fuzi = Weapon('斧子',60)
    # wea.fight(barry,ts) 这样写不好,主体应该是人
    # ts.attack(barry)
    # barry.attack(ts)
    barry.weapon_attack(fuzi)
    # barry对象调用weapon_attack方法,
    # 方法执行的是将斧子对象wea封装到barry对象的属性中、
    # barry.武器 相当于 wea
    barry.武器.fight(barry,ts)
    View Code

    5、面向对象的三大特征                                                                                 

    5.1 继承                                                     

    继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的

    类称为派生类或子类

    python中类的继承分为:单继承和多继承

    复制代码
    class ParentClass1: #定义父类
        pass
    
    class ParentClass2: #定义父类
        pass
    
    class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
        pass
    
    class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
        pass
    View Code

    查看继承

    >>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    (<class '__main__.ParentClass1'>,)
    >>> SubClass2.__bases__
    (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
    View Code

    提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

    >>> ParentClass1.__bases__
    (<class 'object'>,)
    >>> ParentClass2.__bases__
    (<class 'object'>,)

    继承的重要性

    ==========================第一部分
    例如
    
      猫可以:爬树、吃、喝、拉、撒
    
      狗可以:看门、吃、喝、拉、撒
    
    如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:
     
    
    #猫和狗有大量相同的内容
    class 猫:
    
        def爬树(self):
            print '爬树'
    
        def 吃(self):
            # do something
    
        def 喝(self):
            # do something
    
        def 拉(self):
            # do something
    
        def 撒(self):
            # do something
    
    class 狗:
    
        def 看门(self):
            print '看门'
    
        def 吃(self):
            # do something
    
        def 喝(self):
            # do something
    
        def 拉(self):
            # do something
    
        def 撒(self):
            # do something
    
    
    
    ==========================第二部分
    上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
    
      动物:吃、喝、拉、撒
    
         猫:爬树(猫继承动物的功能)
    
         狗:看门(狗继承动物的功能)
    
    伪代码如下:
    class 动物:
    
        def 吃(self):
            # do something
    
        def 喝(self):
            # do something
    
        def 拉(self):
            # do something
    
        def 撒(self):
            # do something
    
    # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
    class 猫(动物):
    
        def爬树(self):
            print '爬树'
    
    # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
    class 狗(动物):
    
         def 看门(self):
            print '看门'
    
    
    
    
    ==========================第三部分
    #继承的代码实现
    class Animal:
    
        def eat(self):
            print("%s 吃 " %self.name)
    
        def drink(self):
            print ("%s 喝 " %self.name)
    
        def shit(self):
            print ("%s 拉 " %self.name)
    
        def pee(self):
            print ("%s 撒 " %self.name)
    
    
    class Cat(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed = ''
    
        def爬树(self):
            print '爬树'
    
    class Dog(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed=''
      
        def 看门(self):
            print '看门'
    
    
    
    # ######### 执行 #########
    
    c1 = Cat('小白家的小黑猫')
    c1.eat()
    
    c2 = Cat('小黑的小白猫')
    c2.drink()
    
    d1 = Dog('胖子家的小瘦狗')
    d1.eat()
    
    使用继承来重用代码比较好的例子
    
    继承可以减少代码重用
    View Code

    那么问题又来了,多继承呢?

    • 是否可以继承多个类
    • 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?

    1、Python的类可以继承多个类,Java和C#中则只能继承一个类

    2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

    • 当类是经典类时,多继承情况下,会按照深度优先方式查找
    • 当类是新式类时,多继承情况下,会按照广度优先方式查找

    经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,

    也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

     

     

    class D:
    
        def bar(self):
            print 'D.bar'
    
    
    class C(D):
    
        def bar(self):
            print 'C.bar'
    
    
    class B(D):
    
        def bar(self):
            print 'B.bar'
    
    
    class A(B, C):
    
        def bar(self):
            print 'A.bar'
    
    a = A()
    # 执行bar方法时
    # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
    # 所以,查找顺序:A --> B --> D --> C
    # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
    a.bar()
    
    经典类多继承
    经典类多继承
    class D(object):
    
        def bar(self):
            print 'D.bar'
    
    
    class C(D):
    
        def bar(self):
            print 'C.bar'
    
    
    class B(D):
    
        def bar(self):
            print 'B.bar'
    
    
    class A(B, C):
    
        def bar(self):
            print 'A.bar'
    
    a = A()
    # 执行bar方法时
    # 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
    # 所以,查找顺序:A --> B --> C --> D
    # 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
    a.bar()
    
    新式类多继承
    新式类多继承

    经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,

    如果D类中么有,则继续去C类中找,如果还是未找到,则报错

    新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,

    如果C类中么有,则继续去D类中找,如果还是未找到,则报错

    注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

  • 相关阅读:
    10.16作业
    day0402作业
    day04作业
    10.13作业
    JVM原理最全、清晰、通俗讲解
    Java的SimpleDateFormat,DateTimeFormatter:YYYY与yyyy
    从源码层面理解 ArrayList 扩容策略
    哈夫曼树
    面向对象三大基本特性,五大基本原则
    数据结构
  • 原文地址:https://www.cnblogs.com/bowen-li/p/s155379.html
Copyright © 2011-2022 走看看