Python学习笔记(一)

Python基础注意点

  1. 字符串是以单引号'或双引号"括起来的任意文本,比如'abc'"xyz"等等。

  2. 如果'本身也是一个字符,那就可以用""括起来,比如"I'm OK"包含的字符是I'm,空格,OK这6个字符

  3. 如果字符串内部既包含'又包含"怎么办?可以用转义字符\来标识,比如:

    1
    'I\'m \"OK\"!'

    表示的字符串内容是:

    1
    I'm "OK"!
  4. 空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值

  5. list与tuple

    • list是一种有序的集合,可以随时添加和删除其中的元素,表示如classmates = ['Michael', 'Bob', 'Tracy']
    • tuple和list非常类似,但是tuple一旦初始化就不能修改,表示如classmates = ('Michael', 'Bob', 'Tracy')

    不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。

    • 只有1个元素的tuple定义时必须加一个逗号,,不然定义的不是tuple,是元素本身

Python函数注意点

  • 必选参数在前,默认参数在后,否则Python的解释器会报错

  • 可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple,方式为*args

  • 而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict,方式为**kw

  • 使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数

  • 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

  • 对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的

高级特性

  • 生成器

    generator:一边循环一边计算的机制

    • 创建生成器

      1. 把一个列表生成式的[]改成()

        1
        2
        3
        4
        5
        6
        >>> 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>
      2. 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

        generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行

        1
        2
        3
        4
        5
        6
        7
        def odd():
        print('step 1')
        yield 1
        print('step 2')
        yield(3)
        print('step 3')
        yield(5)

        调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        >>> o = odd()
        >>> next(o)
        step 1
        1
        >>> next(o)
        step 2
        3
        >>> next(o)
        step 3
        5
        >>> next(o)
        Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        StopIteration
  • 迭代器

    1. 凡是可作用于for循环的对象都是Iterable类型;
    2. 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
    3. 集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象
  • 高阶函数

    把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式

    1
    2
    def add(x, y, f):
    return f(x) + f(y)
    1
    2
    >>> add(-5, 6, abs)
    11
  • map/reduce

    • map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回

      把list所有数字转为字符串:

      1
      2
      >>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
      ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    • reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

      1
      reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

      如把str转换为int

      1
      2
      3
      4
      5
      6
      7
      8
      9
      >>> from functools import reduce
      >>> def fn(x, y):
      ... return x * 10 + y
      ...
      >>> def char2num(s):
      ... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
      ...
      >>> reduce(fn, map(char2num, '13579'))
      13579
  • filter

    1
    `filter()`把传入的函数依次作用于每个元素,然后根据返回值是`True`还是`False`决定保留还是丢弃该元素
    1
    例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
    1
    2
    3
    4
    5
    def is_odd(n):
    return n % 2 == 1
    list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
    # 结果: [1, 5, 9, 15]

    注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list

  • sorted

    1
    按绝对值大小排序:
    1
    2
    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
  • 返回函数

    一个函数可以返回一个计算结果,也可以返回一个函数

    返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。

    闭包:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def foo():
    m=3
    n=5
    def bar():
    a=4
    return m+n+a
    return bar
    >>>bar = foo()
    >>>bar()
    12

    bar在foo函数的代码块中定义。我们称bar是foo的内部函数。

    在bar的局部作用域中可以直接访问foo局部作用域中定义的m、n变量。
    简单的说,这种内部函数可以使用外部函数变量的行为,就叫闭包

  • 偏函数

    1
    当函数的参数个数太多,需要简化时,使用`functools.partial`可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单
    1
    2
    3
    4
    5
    6
    >>> import functools
    >>> int2 = functools.partial(int, base=2)
    >>> int2('1000000')
    64
    >>> int2('1010101')
    85

模块

  • 在Python中,一个.py文件就称之为一个模块(Module)

  • 每一个包目录下面都会有一个__init__.py的文件,__init__.py本身就是一个模块,模块名为包名

  • 类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc

    之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量

面向对象

  • 在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

  • 限制实例的属性:定义一个特殊的__slots__变量,来限制该class实例能添加的属性

    1
    2
    class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

    然后,我们试试:

    1
    2
    3
    4
    5
    6
    7
    >>> 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'
  • 把一个getter方法变成属性,可以加上@property,把一个setter方法变成属性赋值可以加上@score.setter

    如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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

    外部调用

    1
    2
    3
    >>> s = Student()
    >>> s.score = 60 # OK,实际转化为s.set_score(60)
    >>> s.score # OK,实际转化为s.get_score()

  • Python允许使用多重继承,这种设计通常称之为MixIn

  • 动态创建类type()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> def fn(self, name='world'): # 先定义函数
    ... print('Hello, %s.' % name)
    ...
    >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
    >>> h = Hello()
    >>> h.hello()
    Hello, world.
    >>> print(type(Hello))
    <class 'type'>
    >>> print(type(h))
    <class '__main__.Hello'>

    要创建一个class对象,type()函数依次传入3个参数:

    1. class的名称;
    2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

IO编程

  • 文件读写

    在Python中,文件读写是通过open()函数打开的文件对象完成的。在Python中这种对象统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等

    使用with语句操作文件IO是个好习惯,它自动帮我们调用close()方法,如:

    1
    2
    with open('/path/to/file', 'r') as f:
    print(f.read())
  • 多进程

    Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

    子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Adison wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!