• Python基础理论


     

    一 面向对象基本理论

    面向过程:核心是过程,过程就是解决问题的步骤,即先干什么,再干什么

    基于面向过程设计程序,就好比在设计一条流水线,是一种机械思维方法

      优点:复杂的问题简单化

      缺点:可扩展性差(牵一发而动全身)

      应用场景:扩展性低的场合,如linux内核,httpd,git

    面向对象:核心是对象,要理解对象应该把自己当成上帝,在上帝眼中一切存在的事物都是对象,不存在也可以创建出来

      优点:可扩展性强

      缺点:无法像面向过程一样准确地知道什么阶段发生什么事,会有什么结果

      应用场景:与用户交互多的,公司内部的软件,游戏,互联网软件

     

    特征与技能的结合体就是一个对象,一系列对象共有的特征(变量的定义)与技能(函数的定义)的结合体就是类

    在Python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体

     

    类的定义:类是一系列对象共有的特征(变量的定义)与技能(函数的定义)的结合体

    PS:在Python3中统一了类型与类的概念

    定义类语法:

    class 类名:

        '''注释'''

        类体(可以是任意代码,在定义阶段就执行,产生了作用域的概念)

    类的使用

    1.  属性的引用,定义类会执行,生成名称空间

      - 数据属性:Chinese.country

      - 函数属性:Chinese.talk('123')  # 需要几个参数,就得传几个参数,如self 

    2.  实例化,类是抽象的,实例化后变为对象

      - p = Chinese()

     

    对象的使用:只有一种,就是属性引用

      - p.country

      - p.talk()   # 会把p当为一个参数传入talk(self)

     

    对象可以有自己的特征值

      def __init__(self, name, age, sex):

        self.name = name

        self.age = age

        self.sex = sex

     

    类实例化本质

    p1 = Chinese('Linda', 18, 'female')    # Chinese.__init__(p1, 'Linda', 18, 'female')

    self=p1, p1.name=name, p1.age=age, p1.sex=sex 

     

    类名称空间

      - print(Chinese.__dict__)   # 属性字典

    对象名称空间

      - print(p1.__dict__)  # 属性字典

    print(p1.name)  # 等价于 p1.__dict__['name'],本质以变量名的字符串作为key,去调用__dict__,取得值
    print(p1.__dict__['name'])

    对象使用属性时,先在自己对象的名称空间,再找所属类的名称空间

    小结:

    1.  类具有数据属性和函数属性,而对象只有数据属性;但是对象可以调用类的方法

      - Chinese.talk(123)  # 类调用时,和普通方法一样,需几个参数,就传几个参数

      - p1.talk()   # 绑定方法,绑定的含义就是p1默认作为一个参数传入,Chinese.talk(p1)

                              谁来用,就是谁的效果,p1.talk()就是p1的效果,self,__main__.Chinese object

    2.  定义在类内部的变量,是所有对象共有的,id全一样

    3.  定义在类内部的函数,是绑定到所有对象的,是给对象来用,obj.func()会把obj本身当做一个参数

    二  封装、继承、多态

    什么是继承

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

    class ParentClass(object):

        pass

    class SubClass(ParentClass):

        pass

    print(SubClass.__bases__)

    抽象即抽取类似或者说比较像的部分

    抽象分成两个层次:

    1.  将奥巴马和梅西这俩抽象比较像的部分抽取成类;

    2.  将人、猪、狗这三个类比较像的部分抽取成父类。

    抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

    继承:是基于抽象的结果,通过编程语言去实现它,肯定先经历抽象这个过程,

    才能通过继承的方式去表达抽象的结构。

    继承的好处一:减少冗余代码

    class People(Animal):

        def __init__(self, name, age, sex, education):

            Animal.__init__(self, name, age, sex)

            self.education = education  

    在子类定义新的属性,覆盖掉父类的属性,称为覆盖,override

    s.bar(), 顺序:对象自己的__dict__,类的__dict__,父类的__dict__,....

    继承反映的是 一种什么是什么的关系

    组合反映的是 一种什么有什么的关系

    继承的实现原理  - Python3  

    '''
    E   D
    A   B   C
        F
    Python3 广度优先:F - A - E - B - D - C
    '''

    在子类调用父类的方法

    super(自己类名, self).__init__()  # 父类的__init__()

    使用super()调用的所有属性,都是从MRO列表当前的位置往后找。

    多态

      同一种事物的多种形态,指的是父类

      people_obj1.talk()
      pig_obj1.talk()
      dog_obj1.talk()
      
         def func(obj):
        obj.talk()

    好处:同一种调用方式,传入不同的对象,实现不同功能

    封装不是单纯的隐藏

    封装数据的主要原因:保护隐私

    封装方法的主要原因:隔离复杂度

     

    封装其实分为两个层面,但无论那种层面的封装,都要对外界提供访问你内部隐藏内容的接口

    第一层面的封装(什么都不用做):创建类和对象分别创建二者的名称空间,我们只能用类名.或者obj.的方式访问里面的名字,这本身就是一种封装

    第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只能在类的内部使用、外部无法访问,或者留下少量(函数)供外部访问

    在Python中用双下划线的方式实现隐藏属性(设置成私有的)

     

    语法层面,定义__x时,自动变为_People__x,这种自动变形的特点:

    1.  类中定义的__x只能在内部使用,如self.__x,引用的是变形后的结果

    2.  这种变形其实是针对外部的变形,在外部是无法通过__x这个名字访问到的

    3.  在子类定义的__x不会覆盖父类定义的__x

     

    property 作为类函数的装饰器,把类的函数属性,变为数据属性

    class Foo(object):
        @property
        def test(self):
            print('from Foo.test')
    
    obj = Foo()
    obj.test

    也是一种封装,当test为名词时,更适合作为属性,而不是方法

    被@property装饰的函数,是不能直接赋值的,得通过@test.setter定义单独函数来设置

    class People(object):
        def __init__(self, name):
            self.__name =name
    
        @property
        def name(self):
            return self.__name
    
        @name.setter
        def name(self,value):
            self.__name = value
    
        @name.deleter
        def name(self):
            del self.__name
    
    
    p1 = People('Linda')
    print(p1.name)
    
    p1.name = 'Alex' 
    print(p1.name)
    
    del p1.name
    print(p1.name)   # 报错

    绑定方法与非绑定方法 

    在类内部定义的,只要没有装饰器,就是绑定到对象的方法,给对象用的,对象默认作为第一个参数传入

    在类内部定义的,有@classmethod装饰器,是绑定到类的方法,给类用的,类默认作为第一个参数传入

    class Foo(object):
        @classmethod
        def test(cls):
            print(cls)
    
    f = Foo()
    Foo.test()  # 类作为第一个参数传进去,结果为:<class '__main__.Foo'>
    f.test()    # 即使是对象调用,也是把所属类传进去,结果为:<class '__main__.Foo'>

    在类的定义定义的,有@staticmethod装饰器,就不是绑定方法了,不存在默认传值的问题,定义几个参数,就传几个参数即可

    class Foo(object):
        @staticmethod
        def test():
            print('from Foo.test')
    
    Foo.test()  # from Foo.test
    f = Foo()
    f.test()    # from Foo.test

    分析小结:

    class Foo(object):
        def test1(self):
            pass
    
        @classmethod
        def test2(cls):
            pass
    
        @staticmethod
        def test3():
            pass
    
    f = Foo()
    print(f.test1)    # <bound method Foo.test1 of <__main__.Foo object at 0x000000D539EB55F8>>
    print(Foo.test2)  # <bound method Foo.test2 of <class '__main__.Foo'>>
    print(f.test3)    # <function Foo.test3 at 0x000000D539EA6D08>
    print(Foo.test3)  # <function Foo.test3 at 0x000000D539EA6D08>

     小例子:

    import settings
    import hashlib
    import time
    
    class MySQL(object):
        def __init__(self, host, port):
            self.id = self.create_id()  # create_id()具有生成唯一id的功能,普通工具包而已
            self.host = host
            self.port = port
            print('connecting...')
    
        @classmethod
        def from_conf(cls):
            return cls(settings.HOST, settings.PORT)  # MySQL('127.0.0.1', 3306),cls功能:实例化,得到对象
    
        @staticmethod    # 非绑定方法,普通工具包,不需传入self或者cls
        def create_id():
            m = hashlib.md5(str(time.clock()).encode('utf-8'))
            return m.hexdigest()
    
        def select(self):
            print('select...')
    
    conn1 = MySQL('192.168.0.1', 3306)  # 第一种初始化,用户输入
    conn2 = MySQL.from_conf()           # 第二种初始化,从配置文件读取
    面向对象高级部分

    1.  isinstance(obj, cls)与issubclass(sub, super)

    isinstance(obj, cls)检查obj是否是类cls的对象

    issubclass(sub, super)检查sub是否是类super的子类

    2.  反射

    反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

    Python面向对象的反射:通过字符串的形式操作对象相关属性。Python中一切事物都是对象(都可以使用发射)

    四个可以实现自省的函数,下列方法适用于类和对象

    # 判断object中有没有一个name字符串对应的方法或属性
    hasattr(object,name)
    # name作为字符串,获取object的属性或方法
    getattr(object, name, default=None)
    # 设置对象x的属性y=v
    setattr(x, y, v)
    # 删除对象x的属性y
    delattr(x, y)
    class Chinese(object):
        country = 'China'
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    p = Chinese('Linda', 18)
    print(p.name)  # p.__dict__('name')
    # p.x            # 报错 'Chinese' object has no attribute 'x'
    print(hasattr(p, 'x'))   # False
    
    # 设置
    setattr(p, 'x', 112233)  # {'name': 'Linda', 'x': 112233, 'age': 18}
    print(p.__dict__)
    
    # 检测、获取
    if hasattr(p, 'x'):
        res = getattr(p, 'x')
        print(res)   # 112233
    
    # 删除
    delattr(p, 'x')
    print(p.__dict__)  # {'name': 'Linda', 'age': 18}
    四个方法的使用演示
    class Foo(object):
        static_field = "static_filed"
    
        def __init__(self):
            self.name = 'Linda'
    
        def func(self):
            return 'func'
    
        @staticmethod
        def bar():
            return 'bar'
    
    print(getattr(Foo, 'static_field'))
    print(getattr(Foo, 'func'))
    print(getattr(Foo, 'bar'))
    类也是对象
    import sys
    
    def s1():
        print('s1')
    
    def s2():
        print('s2')
    
    this_module = sys.modules[__name__]
    
    print(hasattr(this_module, 's1'))
    print(getattr(this_module, 's2'))
    反射模块当前成员
    """
    程序目录:
    module_test.py
    index.py
    
    当前文件:
    index.py
    """
    
    import module_test as obj
    
    # obj.test()
    
    print(hasattr(obj, 'test'))
    
    getattr(obj,'test')()
    导入其他模块,利用反射查找该模块是否存在某个方法

    反射的好处,可以事先定义好接口,接口只有在被完成后才会真正执行

    程序员A开发了一个ftp客户端,但是没有实现get方法,程序员B得调用这个客户端

    # ftpclient.py
    class FtpClient(object):
        def __init__(self, addr):
            self.addr = addr
            print('正在连接[%s]...' % self.addr)
    
        # def get(self):
        #     print('正在下载...')
    ftpclient.py
    import ftpclient
    f1 = ftpclient.FtpClient('192.168.0.1')
    
    if hasattr(f1, 'get'):
        func = getattr(f1, 'get')
        func()
    print('其他程序1')
    print('其他程序2')
    print('其他程序3')
    print('其他程序4')
    引用ftpclient.py

    类基于用户输入的字符串,反射自己是否有相应的方法,从而自动执行

    class FtpClient(object):
        def __init__(self, addr):
            self.addr = addr
            print('正在连接[%s]....' % self.addr)
    
        def get(self, arg):
            print('正在下载[%s]....' % (arg[1]))
    
        def run(self):
            while True:
                inp = input('>>: ').strip()
                inp_list = inp.split()
                if hasattr(self, inp_list[0]):
                    func = getattr(self, inp_list[0])
                    func(inp_list)
    
    f1 = FtpClient('192.168.0.1')
    f1.run()
    交互式反射应用

    反射另外好处,动态导入模块(基于反射当前模块成员)

    m = __import__('sys')
    print(m.path)
    
    import importlib
    m2 = importlib.import_module('sys')
    print(m2.path)
    动态导入模块

    3.  __str__()

    print(obj) 调用obj.__str__()

    det __str__(self):

      # 必须有返回值,且必须返回字符串类型

      return 'xxxxxxx'

    4.  __del__()

    析构函数,对象被删除的时候,立刻执行obj.__del__(),自动回收内存等,也可以写一些清除数据库连接等操作

    class Foo(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __del__(self):  # 析构函数
            print('del---->')
    
    obj = Foo('Linda', 18)
    # del obj
    print('=========>')
    View Code

    5.  __setitem__(), __getitem__(), __delitem__()

    把对象模拟成字典形式操作,和字典就可以统一处理了

    class Foo(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __getitem__(self, item):
            print('from __getitem__()')
            return self.__dict__[item]
    
        def __setitem__(self, key, value):
            print('from __setitem__')
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            print('from __delitem__')
            self.__dict__.pop(key)
    
    obj = Foo('Linda', 18)
    print(obj['name'])  # 触发self.__getitem__()执行
    
    obj['name']  = 'Jonathan' # 触发self.__setitem__()执行
    print(obj.name)
    
    del obj['name']
    print(obj.name)  # 'Foo' object has no attribute 'name'
    View Code

    6.  __setattr__(), __getattr__(), __delattr__()

    对象操作属性时,触发相应函数

    class Foo(object):
    
        def __setattr__(self, key, value):
            print('form __setattr__')
            self.__dict__[key] = value
    
        def __getattr__(self, item):
            print('from __getattr__')
    
        def __delattr__(self, item):
            print('from __delattr__')
    
    obj = Foo()
    obj.x = 1          # 设置属性时,会触发执行__setattr__()执行
    print(obj.__dict__)
    print(obj.yyyyy)   # 没有属性yyyyy的时候才会触发 __getatrr__()执行
    del obj.x         # 删除属性时,会触发执行__delattr__()执行
    View Code

    7.   加工标准类型

    继承 class List(list):pass

    授权 class Open(object):pass   关键点  def __getattr__():pass

    class Open(object):
        def __init__(self, file_path, mode='r', encoding='utf-8'):
            self.file_path = file_path
            self.mode = mode
            self.encoding = encoding
            self.f = open(self.file_path, mode=self.mode, encoding=self.encoding)
    
        def __getattr__(self, item):
            print(item, type(item))
            return getattr(self.f, item)
    
    
    obj = Open('a.txt',mode='r')
    obj.seek  # seek <class 'str'>
    View Code

    8.  __next__() 和 __iter__()实现迭代器协议

    class Foo(object):
        def __init__(self, n, stop):
            self.n = n
            self.stop = stop
            
        def __next__(self):
            if self.n >= self.stop:
                raise StopIteration
            x = self.n
            self.n += 1
            return x
    
        def __iter__(self):
            return self
    
    
    obj = Foo(0, 100)
    print(next(obj))
    print(next(obj))
    
    from collections import Iterator
    print(isinstance(obj, Iterator))
    
    for i in obj:
        print(i)
    View Code

    9. __doc__()

    class Foo:
        '我是描述信息'
        pass
    
    print(Foo.__doc__)
    它类的描述信息
    class Foo:
        '我是描述信息'
        pass
    
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类
    该属性无法被继承

    10.  __module__(), __class__()

    __module__ 表示当前操作的对象在那个模块

    __class__     表示当前操作的对象的类是什么

    11.  __enter__(), __exit__()

    我们知道在操作文件对象的时候可以这么写

    with open('a.txt') as f:
       '代码块'

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    class Foo(object):
        def __enter__(self):
            print('__enter__')
            return 'aaaa'
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('__exit__')
    
    with Foo() as f:  # f = Foo(),触发def__enter__()执行
        print(f)
        print('----->')
        print('----->')
        print('----->')
        pass
    
    # 最后触发 __exit__()执行
    View Code

    __exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
    
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->不会执行
    View Code

    如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
            print(exc_type)
            print(exc_val)
            print(exc_tb)
            return True
    
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
    print('0'*100) #------------------------------->会执行
    View Code
    class Open:
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.filepath=filepath
            self.mode=mode
            self.encoding=encoding
    
        def __enter__(self):
            # print('enter')
            self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
            return self.f
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            # print('exit')
            self.f.close()
            return True 
        def __getattr__(self, item):
            return getattr(self.f,item)
    
    with Open('a.txt','w') as f:
        print(f)
        f.write('aaaaaa')
        f.wasdf #抛出异常,交给__exit__处理
    练习:模拟Open

    用途或者说好处:

    • 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

    • 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处

    12.  __call__()

    对象后面加括号,触发执行。

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
        def __init__(self):
            pass
    
        def __call__(self, *args, **kwargs):
            print('__call__')
    
    obj = Foo()  # 执行 __init__
    obj()        # 执行 __call__
  • 相关阅读:
    【转】 矩阵构造方法
    CODEVS1187 Xor最大路径 (Trie树)
    POJ2001 Shortest Prefixes (Trie树)
    CODEVS1079 回家 (最短路)
    CODEVS2144 砝码称重2 (哈希表)
    CODEVS1380 没有上司的舞会 (树形DP)
    JAVA 多态和异常处理作业——动手动脑以及课后实验性问题
    再读大道之简第七章第八章
    JAVA 接口与继承作业——动手动脑以及课后实验性问题
    再读大道至简第六章
  • 原文地址:https://www.cnblogs.com/jonathan1314/p/7746520.html
Copyright © 2020-2023  润新知