Python3学习笔记(二)

好久没更新了呀,Python的学习可不能落下!

面向对象编程

面向对象设计思想:抽象出类(Class),根据类创建实例(Instance)。
面向对象的三大特点:封装,继承,多态。

  1. 类和实例:

    • 类的定义:class Student(object): pass Student类继承了object
    • 创建实例:yzl = Student() , 创建实例后,可以自由的给实例绑定属性yzl.age = 18, 仅对本实例有效
    • 类的方法定义:def __init__(self, name, age):, 第一个变量必须是self, 但不用传,表示此实例变量。
  2. 访问限制:以__开头的是私有变量,外部访问不到(但其实可以访问到,只是Python解释器把私有变量换了个名字,比如_Student__name,以_开头的虽然可以访问,但也要当做私有变量,不要轻易访问。总之,Python本身没有任何机制阻止你干坏事,一切全靠自觉。

    class Student(object):
    
    def __init__(self, name, score):
        self.__name = name
        self.__score = score
    
    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))
    
  3. 继承与多态:对于静态语言,比如Java,一个函数的入参类型必须是确定的,而对于动态语言,只要file-like object即可. 鸭子类型:一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
    只要whatever有run()方法就可以。

    def run_twice(whatever):
            whatever.run()
    
  4. 获取对象信息:

    • type() 判断对象类型 type(123):
    • isinstance() 判断一个对象是否是某种类型 isinstance('abc', str): True
    • dir() 获取一个对象的所有属性和方法,它返回一个包含字符串的list
    • hasattr(obj, 'x') 获取属性 setattr(obj, 'y', 19) 设置属性,注意:只有在不知道对象信息的时候,我们才会去获取对象信息
  5. 实例属性和类属性:不通过__init__()构造,直接在class里定义属性的是类属性。实例属性和类属性名字不要一样,否则类属性在当前实例会被覆盖掉。

  6. 限制实例的属性:__slots__ = ('name', 'age') , 定义一个__slots__ 限制类的实例属性只能在tuple里,否则将报AttributeError错误,注意:__slots__只作用于当前类实例,对子类不起作用,若子类也定义了slots,则还要加上父类的slots。

  7. @property : 相当于 getter,@name.setter : 相当于 setter,这两个decorator的目的是让方法变为属性(调用方法生成了属性),可以写出更简短的代码,同时保证对参数进行必要性的检查。

    class Screen(object):
        @property
        def width(self):
            return self.__width
    
        @width.setter
        def width(self, w):
            self.__width = w
    
    s = Screen()
    print(dir(s))  # 结果为: ['__class__', '__delattr__', '__dict__', ...]
    s.width = 1
    print(dir(s))  # 结果为: ['_Screen__width', '__class__', '__delattr__', '__dict__', ...]
    
  8. 多继承:Python支持多继承,class class Dog(Mammal, Runnable):

  9. 定制类:重写class的函数,使之符合我们的需求,以下是常用的定制类:

    • __str__ : 自定义打印实例,变量调用的是__repr__

      class Student(object):
          def __init__(self, name):
              self.name = name
          def __str__(self):
              return 'Student object (name=%s)' % self.name
          __repr__ = __str__
      
    • __iter__: 返回一个迭代对象,通过__next()__方法循环获取下一个值,知道遇到StopIteration错误时推出循环

      class Fib(object):
         def __init__(self):
             self.a, self.b = 0, 1 # 初始化两个计数器a,b
      
         def __iter__(self):
             return self # 实例本身就是迭代对象,故返回自己
      
         def __next__(self):
             self.a, self.b = self.b, self.a + self.b # 计算下一个值
             if self.a > 100000: # 退出循环的条件
                 raise StopIteration();
             return self.a # 返回下一个值
      
    • __getitem__: 按照下标或切片取出元素

      # 重写__getitem__ 支持索引和切片
      class Fib(object):
          def __getitem__(self, n):
              if isinstance(n, int):  # n是索引
                  a, b = 1, 1
                  for x in range(n):
                      a, b = b, a + b
                  return a
              if isinstance(n, slice):  # n是切片
                  start = n.start
                  stop = n.stop
                  if start is None:
                      start = 0
                  a, b = 1, 1
                  L = []
                  for x in range(stop):
                      if x >= start:
                          L.append(a)
                      a, b = b, a + b
                  return L
      
      print(Fib()[9])
      print(Fib()[:10])
      
    • __getattr__: 动态返回一个属性, 可以写一个链式调用

      def __getattr__(self, path):
         return Chain('%s/%s' % (self._path, path))
      
    • __call__: 直接在实例本身上调用, 通过callable()函数,可以判断一个对象是否是“可调用”对象。

      class Student(object):
          def __init__(self, name):
              self.name = name
      
          def __call__(self):
              print('My name is %s.' % self.name)
      
      s = Student('DreamYoung')
      if callable(s):
          s()  # self参数不要传入    
      
  10. 枚举类:Enum 枚举类,把一组相关常量定义在一个class中,class不可变,成员可直接比较

    from enum import Enum, unique
    
    @unique  # @unique装饰器用于检查有没有重复值
    class Weekday(Enum):
        Sun = 0  # Sun的value被设定为0
        Mon = 1, 2
        Tue = 2
    
    print(Weekday.Sun)          # Weekday.Sun
    print(Weekday.Sun.value)    # 0
    print(Weekday.Mon.value)    # (1, 2)
    print(Weekday(2))           # Weekday.Tue
    print(Weekday['Tue'])       # Weekday.Tue
    print(Weekday['Tue'].value) # 2
    

    可见,既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量

  11. 元类:Python的class的定义是运行时创建的,创建的方法就是使用type()函数!type()函数既可以返回一个对象的类型,又可以创建出新的类型,无需使用class关键字。

    def fun(self, name='DreamYoung'):
        print('Hello, ' + name)
        
    Hello = type('Hello', (object,), dict(hello=fun))  # 创建Hello class
    
    h = Hello()
    h.hello()
    

    type()函数需要三个参数:函数名称,继承的父类集合(tuple),方法名绑定的函数。
    Python创建类也是扫一下class关键字,然后通过type创建出来类。
    如果要控制类的行为,可以使用元类�metaclass,可以把类理解为metaclass创建的实例。 先定义metaclass,就可以创建类,最后创建实例。metaclass暂时不会用到,如果需要深入了解,可以参考这篇文章

错误、调试和测试

  1. 错误:Python内置了try...except...finally...用于捕获异常,所有的异常都继承自BaseException, 查看异常继承关系

    注意:

    • except可以有多个,多个except异常 父类会覆盖子类的异常, except后可以跟else
    • finally在最后执行,可以不写
    • 可以使用logging模块记录日志,通过查看调用堆栈,定位排查错误

    写法:

    try:
        print(1/0)
    except ZeroDivisionError as e:
        logging.exception(e)
    else:
        print('else')
    finally:
        print('finally')
    
  2. 调试:要想调试起来爽,要善于使用logging

    logging有debug、info、warning、error几个级别,通过:

    import logging
    logging.basicConfig(level=logging.INFO) # info级别日志,debug日志不会显示
    

    指定当前模块的日志级别,logging的好处是通过简单的配置,日志可以输出到文件等等

  3. 单元测试:编写单元测试需要引入unittest模块,并编写一个从unittest.TestCase继承测试类。
    注意,测试方法必须以test开头(test_xxx()),否则在测试时不会被执行。unittest提供了很多内置条件的判断,比如:assertEqual()assertRaises()等等 用于判断输出值是否是我们的期望值。

    运行前与运行后:编写两个特殊的方法setUp()tearDown(),在每个单元测试方法执行之前执行setUp(),执行之后执行tearDown()。这个用处大大的,比如可以在setUp()方法连接测试库,tearDown()方法关闭连接。

    运行单元测试有两种方法:

    1. 加上两行代码,这样可以当做正常的Python脚步运行:

      if __name__ == '__main__':
          unittest.main()
      
    2. (荐)通过命令行参数直接运行单元测试 :可以批量执行单元测试

      $ python3 -m unittest my_test1.py my_test2.py
      
  4. 文档测试:Python的很多官方文档都是示例代码,通过doctest模块,可以提前并执行文档注释代码。
    比如就绝对值的函数abs()写上这样的注释:

    def abs(n):
        """
        Function to get absolute value of number.
    
        Example:
    
        >>> abs('')
        Traceback (most recent call last):
            ...
        TypeError: unorderable types: str() >= int()
        >>> abs(1)
        1
        >>> abs(-1)
        1
        >>> abs(0)
        0
        """
        return n if n >= 0 else (-n + 1)  # 正确的代码应为: return n if n >= 0 else (-n)
    
    if __name__ == '__main__':
        import doctest
    
        doctest.testmod()
    

    会得到以下输出:


File "xxxx/demo/dream/young/python/Test.py", line 13, in main.abs
Failed example:
abs(-1)
Expected:
1
Got:
2


1 items had failures:
1 of 4 in main.abs
Test Failed 1 failures.


如果没有输出任何信息,说明注释代码没有错误。注意:当模块正常导入时,doctest不会被执行。只有在命令行直接运行时,才执行doctest。所以,不必担心doctest会在非测试环境下执行。

你可能感兴趣的:(Python3学习笔记(二))