python笔记

作者: fishliu | 来源:发表于2018-01-09 08:38 被阅读0次

    python

    基础

    数据类型和变量

    1. 整数
    2. 浮点数
    3. 字符串(转义符 \)
    4. 布尔值(True,False)
      • 布尔值可以用and、or和not运算。
    5. 空值是Python里一个特殊的值,用None
    6. 变量
    7. 常量。在Python中,通常用全部大写的变量名表示常量

    字符串和编码

    1. 一个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制11111111=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是65535,4个字节可以表示的最大整数是4294967295。
    2. ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。
    3. 用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件。

    python字符串

    • 在最新的Python 3版本中,字符串是以Unicode编码的。

    • ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符

    • 由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

    • 以Unicode表示的str通过encode()方法可以编码为指定的bytes。

    • 从网络或磁盘上读取了字节流,那么读到的数据就是bytes。要把bytes变为str,就需要用decode()方法。

    • 计算str包含多少个字符,可以用len()函数。

    • 1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。

    • 由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

          #!/usr/bin/env python3
          # -*- coding: utf-8 -*-
      
    • 格式化字符串

    %运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?

    在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

    >>> 'Hello, %s' % 'world'
    'Hello, world'
    >>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
    'Hi, Michael, you have $1000000.'
    

    常见的占位符有:

    占位符 替换内容
    %d 整数
    %f 浮点数
    %s 字符串
    %x 十六进制整数
    print('%2d-%02d' % (3, 1))
    print('%.2f' % 3.1415926)
    

    另一种格式化字符串的方法是使用字符串的format()方法

    它会用传入的参数依次替换字符串内的占位符{0}、{1}……,不过这种方式写起来比%要麻烦得多:

    >>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
    'Hello, 小明, 成绩提升了 17.1%'
    

    使用list和tuple

    list

    Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素。

    • 用len()函数可以获得list元素的个数
    • 用索引来访问list中每一个位置的元素
    • 当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一个元素的索引是len(classmates) - 1。
    • 如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素
    • list是一个可变的有序表,append可以往list中追加元素到末尾:
    • insert(num,content)也可以把元素插入到指定的位置
    • 要删除list末尾的元素,用pop()方法;要删除指定位置的元素,用pop(i)方法,其中i是索引位置
    • list里面的元素的数据类型也可以不同
    • list元素也可以是另一个list
    >>> classmates = ['Michael', 'Bob', 'Tracy']
    >>> classmates
    ['Michael', 'Bob', 'Tracy']
    >>> len(classmates)
    3
    >>> classmates[0]
    'Michael'
    >>> classmates[1]
    'Bob'
    >>> classmates[2]
    'Tracy'
    >>> classmates[3]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: list index out of range
    >>> classmates.append('Adam')
    >>> classmates
    ['Michael', 'Bob', 'Tracy', 'Adam']
    >>> classmates.insert(1, 'Jack')
    >>> classmates
    ['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']
    >>> classmates.pop()
    'Adam'
    >>> classmates
    ['Michael', 'Jack', 'Bob', 'Tracy']
    >>> classmates.pop(1)
    'Jack'
    >>> classmates
    ['Michael', 'Bob', 'Tracy']
    >>> classmates[1] = 'Sarah'
    >>> classmates
    ['Michael', 'Sarah', 'Tracy']
    

    tuple

    另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改
    只有1个元素的tuple定义时必须加一个逗号

    >>> t = (1,)
    >>> t
    (1,)
    
    >>> t = ('a', 'b', ['A', 'B'])
    >>> t[2][0] = 'X'
    >>> t[2][1] = 'Y'
    >>> t
    ('a', 'b', ['X', 'Y'])
    

    条件判断

    根据Python的缩进规则,如果if语句判断是True,就把缩进的两行print语句执行了,否则,什么也不做。
    也可以给if添加一个else语句,意思是,如果if判断是False,不要执行if的内容,去把else执行了.

    • 注意不要少写了冒号:
    • 可以用elif做更细致的判断
    age = 3
    if age >= 18:
        print('your age is', age)
        print('adult')
    else:
        print('your age is', age)
        print('teenager')
    
    • 用input()读取用户的输入,这是因为input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数。Python提供了int()函数来完成这件事情:
    s = input('birth: ')
    birth = int(s)
    if birth < 2000:
        print('00前')
    else:
        print('00后')
    

    循环

    Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来

    names = ['Michael', 'Bob', 'Tracy']
    for name in names:
        print(name)
    

    range(101)就可以生成0-100的整数序列

    第二种循环是while循环,只要条件满足,就不断循环,条件不满足时退出循环。比如我们要计算100以内所有奇数之和,可以用while循环实现

    sum = 0
    n = 99
    while n > 0:
        sum = sum + n
        n = n - 2
    print(sum)
    
    • break语句可以提前退出循环
    • 可以通过continue语句,跳过当前的这次循环

    使用dict和set

    dict

    Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。

    • 通过in判断key是否存在
    • dict提供的get()方法,如果key不存在,可以返回None,或者自己指定的value
    • 删除一个key,用pop(key)方法,对应的value也会从dict中删除
    >>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    >>> d['Michael']
    95
    >>> 'Thomas' in d
    False
    >>> d.get('Thomas')
    >>> d.get('Thomas', -1)
    -1
    >>> d.pop('Bob')
    75
    

    set

    set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
    要创建一个set,需要提供一个list作为输入集合

    • 通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果
    • 通过remove(key)方法可以删除元素
    • set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作
    >>> s = set([1, 1, 2, 2, 3, 3])
    >>> s
    {1, 2, 3}
    >>> s.add(4)
    >>> s
    {1, 2, 3, 4}
    >>> s.add(4)
    >>> s
    {1, 2, 3, 4}
    >>> s.remove(4)
    >>> s
    {1, 2, 3
    >>> s1 = set([1, 2, 3])
    >>> s2 = set([2, 3, 4])
    >>> s1 & s2
    {2, 3}
    >>> s1 | s2
    {1, 2, 3, 4}
    

    函数

    Python内置了很多有用的函数,我们可以直接调用。

    调用函数

    要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数。可以直接从Python的官方网站查看文档:

    http://docs.python.org/3/library/functions.html#abs

    也可以在交互式命令行通过help(abs)查看abs函数的帮助信息。
    调用abs函数:

    >>> abs(100)
    100
    >>> abs(-20)
    20
    >>> abs(12.34)
    12.34
    

    定义函数

    在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

    def my_abs(x):
        if x >= 0:
            return x
        else:
            return -x
    

    如果想定义一个什么事也不做的空函数,可以用pass语句:

    def nop():
        pass
    

    数据类型检查可以用内置函数isinstance()实现

    def my_abs(x):
        if not isinstance(x, (int, float)):
            raise TypeError('bad operand type')
        if x >= 0:
            return x
        else:
            return -x
    

    返回多个值

    import math
    # import math语句表示导入math包,并允许后续代码引用math包里的sin、cos等函数。
    def move(x, y, step, angle=0):
        nx = x + step * math.cos(angle)
        ny = y - step * math.sin(angle)
        return nx, ny
    >>> x, y = move(100, 100, 60, math.pi / 6)
    >>> print(x, y)
    151.96152422706632 70.0
    

    但其实这只是一种假象,Python函数返回的仍然是单一值:

    >>> r = move(100, 100, 60, math.pi / 6)
    >>> print(r)
    (151.96152422706632, 70.0)
    

    原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。

    函数的参数

    • 位置参数
    • 默认参数
    • 可变参数*
    def calc(*numbers):
        sum = 0
        for n in numbers:
            sum = sum + n * n
        return sum
    
    • 关键字参数**
    • 命名关键字参数

    和关键字参数kw不同,命名关键字参数需要一个特殊分隔符后面的参数被视为命名关键字参数。
    如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
    命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错.
    命名关键字参数可以有缺省值,从而简化调用.

    高级特性

    切片

    取一个list、tuple、str的部分

    >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
    >>> L[:3]
    ['Michael', 'Sarah', 'Tracy']
    >>> L[1:3]
    ['Sarah', 'Tracy']
    >>> L[-2:]
    ['Bob', 'Jack']
    >>> L[-2:-1]
    ['Bob']
    >>> L = list(range(100))
    >>> L
    [0, 1, 2, 3, ..., 99]
    # 前10个数,每两个取一个:
    >>> L[:10:2]
    [0, 2, 4, 6, 8]
    >>> 'ABCDEFG'[:3]
    'ABC'
    >>> 'ABCDEFG'[::2]
    'ACEG'
    

    迭代

    在Python中,迭代是通过for ... in来完成的
    如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断

    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
    >>> isinstance([1,2,3], Iterable) # list是否可迭代
    True
    >>> isinstance(123, Iterable) # 整数是否可迭代
    False
    

    列表生成式

    运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:

    >>> import os # 导入os模块,模块的概念后面讲到
    >>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
    ['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']
    

    生成器

    要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator

    • 通过next()函数获得generator的下一个返回值
    >>> L = [x * x for x in range(10)]
    >>> L
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> g = (x * x for x in range(10))
    >>> g
    <generator object <genexpr> at 0x1022ef630>
    >>> next(g)
    0
    >>> next(g)
    1
    >>> next(g)
    4
    >>> next(g)
    9
    >>> next(g)
    16
    >>> next(g)
    25
    >>> next(g)
    36
    >>> next(g)
    49
    >>> next(g)
    64
    >>> next(g)
    81
    >>> next(g)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    
    • generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
    • 用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
    >>> g = fib(6)
    >>> while True:
    ...     try:
    ...         x = next(g)
    ...         print('g:', x)
    ...     except StopIteration as e:
    ...         print('Generator return value:', e.value)
    ...         break
    ...
    g: 1
    g: 1
    g: 2
    g: 3
    g: 5
    g: 8
    Generator return value: done
    

    迭代器

    可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如list、tuple、dict、set、str等;

    一类是generator,包括生成器和带yield的generator function。

    这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

    可以使用isinstance()判断一个对象是否是Iterable对象:

    >>> from collections import Iterable
    >>> isinstance([], Iterable)
    True
    >>> isinstance({}, Iterable)
    True
    >>> isinstance('abc', Iterable)
    True
    >>> isinstance((x for x in range(10)), Iterable)
    True
    >>> isinstance(100, Iterable)
    False
    

    而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
    可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
    可以使用isinstance()判断一个对象是否是Iterator对象:

    >>> from collections import Iterator
    >>> isinstance((x for x in range(10)), Iterator)
    True
    >>> isinstance([], Iterator)
    False
    >>> isinstance({}, Iterator)
    False
    >>> isinstance('abc', Iterator)
    False
    

    把list、dict、str等Iterable变成Iterator可以使用iter()函数

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

    函数式编程

    函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

    高阶函数

    • Python内建了map()和reduce()函数,他们都是高阶函数。
    >>> from functools import reduce
    >>> def fn(x, y):
    ...     return x * 10 + y
    ...
    >>> def char2num(s):
    ...     digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    ...     return digits[s]
    ...
    >>> reduce(fn, map(char2num, '13579'))
    
    • Python内建的filter()函数用于过滤序列
    def not_empty(s):
        return s and s.strip()
    
    list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
    # 结果: ['A', 'B', 'C']
    
    • sorted
      Python内置的sorted()函数就可以对list进行排序:
    >>> sorted([36, 5, -12, 9, -21])
    [-21, -12, 5, 9, 36]
    # 此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序
    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
    # 默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
    # 我们给sorted传入key函数,即可实现忽略大小写的排序
    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
    ['about', 'bob', 'Credit', 'Zoo']
    # 要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True:
    >>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
    ['Zoo', 'Credit', 'bob', 'about']
    

    返回函数

    高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

    def lazy_sum(*args):
        def sum():
            ax = 0
            for n in args:
                ax = ax + n
            return ax
        return sum
    

    闭包

    匿名函数

    关键字lambda表示匿名函数,冒号前面的x表示函数参数。
    匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
    用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

    >>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    装饰器

    在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
    本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:

    def log(func):
        def wrapper(*args, **kw):
            print('call %s():' % func.__name__)
            return func(*args, **kw)
        return wrapper
    # 观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
    @log
    def now():
        print('2015-3-25')
    # 调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志
    >>> now()
    call now():
    2015-3-25
    

    不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

    偏函数

    Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。要注意,这里的偏函数和数学意义上的偏函数不一样。
    functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

    >>> import functools
    >>> int2 = functools.partial(int, base=2)
    >>> int2('1000000')
    64
    >>> int2('1010101')
    85
    

    模块

    现在,假设我们的abc和xyz这两个模块名字与其他模块冲突了,于是我们可以通过包来组织模块,避免冲突。方法是选择一个顶层包名,比如mycompany,按照如下目录存放:
    mycompany
    ├─__init__.py
    ├─ abc.py
    └─ xyz.py
    引入了包以后,只要顶层的包名不与别人冲突,那所有模块都不会与别人冲突。现在,abc.py模块的名字就变成了mycompany.abc,类似的,xyz.py的模块名变成了mycompany.xyz。
    请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是mycompany。

    面向对象编程

    类和实例

    在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。
    在Python中,定义类是通过class关键字:

    class Student(object):
        pass
    >>> bart = Student()
    >>> bart
    <__main__.Student object at 0x10a67a590>
    >>> Student
    <class '__main__.Student'>
    # 由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去:
    class Student(object):
        def __init__(self, name, score):
            self.name = name
            self.score = score
    # 注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
    
    

    访问限制

    从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性
    可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:

      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))
    

    继承和多态

    # 编写了一个名为Animal的class,有一个run()方法可以直接打印:
    class Animal(object):
        def run(self):
            print('Animal is running...')
    # 当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:
    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
    
    a = list() # a是list类型
    b = Animal() # b是Animal类型
    c = Dog() # c是Dog类型
    # 判断一个变量是否是某个类型可以用isinstance()判断:
    >>> isinstance(a, list)
    True
    >>> isinstance(b, Animal)
    True
    >>> isinstance(c, Dog)
    True
    >>> isinstance(c, Animal)
    True
    

    获取对象信息

    • 基本类型都可以用type()判断:
    >>> type(123)
    <class 'int'>
    >>> type('str')
    <class 'str'>
    >>> type(None)
    <type(None) 'NoneType'>
    

    判断基本数据类型可以直接写int,str等,但如果要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量:

    >>> import types
    >>> def fn():
    ...     pass
    ...
    >>> type(fn)==types.FunctionType
    True
    >>> type(abs)==types.BuiltinFunctionType
    True
    >>> type(lambda x: x)==types.LambdaType
    True
    >>> type((x for x in range(10)))==types.GeneratorType
    True
    
    • 对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

    使用dir()

    如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

    >>> dir('ABC')
    ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
    

    仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态

    >>> class MyObject(object):
    ...     def __init__(self):
    ...         self.x = 9
    ...     def power(self):
    ...         return self.x * self.x
    ...
    >>> obj = MyObject()
    >>> hasattr(obj, 'x') # 有属性'x'吗?
    True
    >>> obj.x
    9
    >>> hasattr(obj, 'y') # 有属性'y'吗?
    False
    >>> setattr(obj, 'y', 19) # 设置一个属性'y'
    >>> hasattr(obj, 'y') # 有属性'y'吗?
    True
    >>> getattr(obj, 'y') # 获取属性'y'
    19
    >>> obj.y # 获取属性'y'
    19
    

    实例属性和类属性

    • 如果Student类本身需要绑定一个属性呢?可以直接在class中定义属性,这种属性是类属性,归Student类所有
    • 给实例绑定属性的方法是通过实例变量,或者通过self变量

    面向对象高级编程

    • 使用__slots__
      正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:
    class Student(object):
        pass
    # 尝试给实例绑定一个属性
    >>> s = Student()
    >>> s.name = 'Michael' # 动态给实例绑定一个属性
    >>> print(s.name)
    Michael
    >>> def set_age(self, age): # 定义一个函数作为实例方法
    ...     self.age = age
    ...
    >>> from types import MethodType
    >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    >>> s.set_age(25) # 调用实例方法
    >>> s.age # 测试结果
    25
    # 为了给所有实例都绑定方法,可以给class绑定方法:
    >>> def set_score(self, score):
    ...     self.score = score
    ...
    >>> Student.set_score = set_score
    

    想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
    为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class实例能添加的属性

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    >>> s = Student() # 创建新的实例
    >>> s.name = 'Michael' # 绑定属性'name'
    >>> s.age = 25 # 绑定属性'age'
    >>> s.score = 99 # 绑定属性'score'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'score'
    

    使用slots要注意,slots定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
    除非在子类中也定义slots,这样,子类实例允许定义的属性就是自身的slots加上父类的slots

    使用@property

    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, value):
            if not isinstance(value, int):
                raise ValueError('score must be an integer!')
            if value < 0 or value > 100:
                raise ValueError('score must between 0 ~ 100!')
            self._score = value
    # @property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作
    

    多重继承

    class Animal(object):
        pass
    
    # 大类:
    class Mammal(Animal):
        pass
    
    class Bird(Animal):
        pass
    
    # 各种动物:
    class Dog(Mammal):
        pass
    
    class Bat(Mammal):
        pass
    
    class Parrot(Bird):
        pass
    
    class Ostrich(Bird):
        pass
    
    class Runnable(object):
        def run(self):
            print('Running...')
    
    class Flyable(object):
        def fly(self):
            print('Flying...')
    
    class Dog(Mammal, Runnable):
        pass
    
    

    MixIn

    在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。
    Python自带的很多库也使用了MixIn。举个例子,Python自带了TCPServer和UDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn和ThreadingMixIn提供。通过组合,我们就可以创造出合适的服务来。

    定制类

    相关文章

      网友评论

        本文标题:python笔记

        本文链接:https://www.haomeiwen.com/subject/ddfwnxtx.html