廖雪峰Python教程笔记

这是我在 jupyter notebook 上做的笔记,大家可以在 jupyter notebook 上敲出来

创建只有一个值的 tuple

t = (1,)
t

不可变对象调用方法

对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。

a = 'abc'
b = a.replace('a', 'A')
a, b

使用python的下标循环

for i, name in enumerate(['yang', 'cao', 'huang', 'yin']):
    print(i, name)

全排列

列表生成式两层循环

# 第一个for循环可以看成是外层循环
[m + n for m in 'ABC' for n in 'XYZ']

python中的三目运算符

x = 1
x if x % 2 == 0 else -x # 是一个表达式
[x if x % 2 == 0 else -x for x in range(1, 11)]

生成器

# 定义方式1:类似列表生成式
g = (x * x for x in range(0, 10))
for n in g:
    print(n)
# 定义方式2:将函数中的print转换成yield
def fib(max):
    # 这不是一个普通函数,而是一个generator
    # 而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
f = fib(6) # generator函数的“调用”实际返回一个generator对象
f
next(f)
next(f)
next(f)

用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

g = fib(6)
while True:
    try:
        x = next(g)
        print('g:', x)
    except StopIteration as e:
        print('Generate return value', e.value)
        break

可迭代对象和迭代器

可以直接作用于for循环的对象统称为可迭代对象:Iterable。需要实现__iter__方法。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。需要实现__iter__方法和__next__方法。

迭代器是可迭代对象的一个子集。

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

isinstance(iter([1, 2, 3]), Iterator) # 把Iterator变成Iterable

迭代器与生成器的区别

生成器是迭代器的一个子集,是一种特殊的迭代器。

生成器分为包含yield的函数和生成器表达式,但是我们还可以自己定义一个类来实现迭代器:

class Iterat(object):
    def __init__(self, array):
        self.x = array
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index < len(self.x):
            value = self.x[self.index]
            self.index += 1
        else:
            raise StopIteration
        return value
    
it = Iterat([1, 2, 3, 4, 5])
print(type(it))
for i in it:
    print(i)

函数名是一个变量

函数是一个对象,函数名是指向函数的一个变量。

def now():
    print('2015-3-25')

f = now # now是指向此函数对象的一个变量,将now赋值给f,意思就是将f也指向这个函数对象
f()

高阶函数

一个函数可以接受另一个函数作为参数,这种函数称之为高阶函数。

def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs))

闭包

什么是闭包:外部函数返回的不是一个具体的值,而是一个函数。
外部函数的参数和变量都保存在返回的函数中。

为什么要有闭包:闭包存在的意义就是它夹杂了外部变量(私货),同一个函数夹杂了不同的私货,就实现了不同的功能。

对于下面这个例子,在count函数中,返回的函数f并没有在count运行过程中立即执行,而是在调用了f1,f2,f3后才执行,而这时i已经变成了3

def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i
        fs.append(f)
    return fs

f1, f2, f3 = count()
f1(), f2(), f3()

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count():
    def f(j):
        def g():
            return j * j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

f1, f2, f3 = count()
f1(), f2(), f3()

nonlocal与global

def func():
    a = 1
    def g():
        a = a + 1 # python解析器认为要在内部函数中创建一个新的局部变量,这时外部函数的a实际已不可见,
                  # 而解析器右边对a加1就是用本地的局部变量a加1,而这时左边的a即本地的变量a还没有创建(等右边赋值呢),
                  # 因此就这就产生了一个是鸡生蛋还是蛋生鸡的问题
    g()
    return a
func()
def func():
    a = 1
    def g():
        nonlocal a
        a = a + 1 # 解决办法即使用nonlocal声明a
    g()
    return a
func()

nonlocal:当需要在嵌套函数中给外部函数定义的变量赋值的时候使用。

global:当需要在函数中给外部变量定义时使用。

偏函数

其实就是创建一个新函数,这个新函数可以往原函数里填一些默认参数,从而调用时简单

import functools
def multiply(a, b):
    return a * b

mul_2 = functools.partial(multiply, b=2) # 填入默认参数b = 2
mul_2(1)
import functools
from functools import reduce
def multiply(*args):
    return reduce(lambda x, y: x * y, args)

mul_6 = functools.partial(multiply, 2, 3) # 填入默认可变参数2, 3
mul_6(1)

对象可绑定任何数据

和静态语言不同,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))
bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.age = 8
bart.age
lisa.age

类的访问控制

__xxx: 私有变量,不能被外部访问

_xxx: 这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

_xxx_: 特殊变量,可以被直接访问

多态

Python的多态跟静态语言不一样。

多态一般应用在函数中,函数的形参是某种类,内部使用了这个类的方法。对于Python来说,只要满足该类有此函数调用的所有方法即可,不要求指定的类型。例子如下:

class Animal(object):
    def run(self):
        print('Animal is running...')
        
class Dog(Animal):
    def run(self):
        print('Dog is running...')

class Timer(object):
    def run(self):
        print('Start...')

def run_twice(animal):
    animal.run()
    animal.run()
run_twice(Animal())
run_twice(Dog())
run_twice(Timer())

MixIn

设计类的继承关系时,通常,主线都是单一继承下来的,如果需要“混入”额外的功能,通过多重继承就可以实现。比如让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。

主要的目的是:更好地看出继承关系,增加代码可读性。继承的第一个类往往是主线,有明显的层次关系,而继承的后面的类往往是功能类,也就是额外的功能。

例如:男人类,单一继承链上他属于人类,如果还要为这个男人类添加其它功能,比如抽烟类,那就用MixIn的写法加进来,但是无需你考虑抽烟类和人类有什么关系。

class Animal(object):
    pass

# 主要的类层次按照哺乳类和鸟类设计
class Mammal(Animal):
    pass

class Bird(Animal):
    pass

# 为类添加额外的功能
class RunnableMixIn(object):
    def run(self):
        print('Running...')

class FlyableMixIn(object):
    def fly(self):
        print('Flying...')

# 各种动物
class Dog(Mammal, RunnableMixIn): # 多重继承,获取多个父类的所有功能
    pass

class Bat(Mammal, FlyableMixIn):
    pass

class Parrot(Bird, FlyableMixIn):
    pass

class Ostrich(Bird, RunnableMixIn):
    pass
d = Dog()
d.run()

关于对象的属性方法优先级问题

对于一个实例来说,有可能存在一种情况,就是属性和方法重名的问题,此时实例是优先调用哪个呢,实验代码如下:

class Student(object):
    def __init__(self, name):
        self.name = name
    
    def name(string):
        print(string)

s = Student()
s.name
s.name('I\'m a String!')

可见,如果属性和方法重名,对象会优先调用属性。

定制类

_str_: 打印类时,给出自定义的信息

_iter_: 变成可迭代对象时定义,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的_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_: 把对象当列表用,可以按照下标取元素

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

_getattr_: 调用不存在的方法或属性时,激活这个方法

_call_: 在实例本身上调用时,激活此方法

class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student('Michael')
s() # self参数不要传入

把方法变成属性调用

@property

在方法上加这个装饰器,当取与这个方法对应的属性时调用

@xxx.setter

xxx是方法的名字,当给这个方法对应的属性赋值时调用

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
s = Student()
s.score = 1000
s.score = 90
s.score

try…except可跨越多层调用

比如函数main()调用bar(),bar()调用foo(),结果foo()出错了,这时,只要main()捕获到了,就可以处理:

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar('0')
    except Exception as e:
        print('Error:', e)
    finally:
        print('finally...')

main()
print(r'ABC\\-001') # 加r,此时Python不考虑转义字符,即都视为普通字符
print('ABC\\-001')

正则表达式

(?:···)

如果想用括号把整个式子看成一个整体,又不想表示一个group,就可以在左括号后面加上"?:"

r + 字符串

一般都会在模式串左边加上r,此时Python不考虑转义字符,这里的转义字符指的是print时候的转义字符,如’\t’等。但是对于正则匹配而言,反斜杠加其他字母可以表示特殊形式,比如\d表示数字,这时的反斜杠便与之前不同了。对于要匹配的字符串(第二个参数),我们一般不会加r,因为它不需要正则表达式的特殊规则,只是一个普通字符串罢了,当然也可以加r,此时反斜杠(\)看成普通字符。

要注意反斜杠可以转义所有元字符,如下:

. ^ $ * + ? { } [ ] \ | ( )

m = re.match(r'\\\\', '\\\\') # 第一项:按照正则表达式的规则,反斜杠转义了反斜杠,最后有两个反斜杠;
                              # 第二项:按照普通字符串的规则,也是反斜杠转义了反斜杠,最后有两个反斜杠
m
m = re.match(r'(\\\\)+', r'\\\\') # 第一项:按照正则表达式的规则,反斜杠转义了反斜杠,括号内有两个反斜杠;
                              # 第二项:四个反斜杠
m

匹配规则

如果加^$,表示这行必须和模式串完全对应才行,也就是整个要匹配的字符串要和模式串对应。
如果不加,要匹配字符串的前缀,符合模式串就算匹配成功。

print(re.match(r'^abc$', 'abcd'))
print(re.match(r'abc', 'abcd'))
print(re.match(r'bcd', 'abcda'))

文件读写模式

‘r’:只读;

‘w’:只写;

‘w+’:读写(截断:原来的没有了);

‘r+’:读写(不截断)

合并拆分路径

合并路径

os.path.join('E:\\Learn\\Code\\python', 'test.txt')

把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名

os.path.split('E:\\Learn\\Code\\python\\test.txt')

直接得到文件扩展名

os.path.splitext('E:\\Learn\\Code\\python\\test.txt')

复制、重命名、删除文件

复制

import shutil

shutil.copyfile('test.txt', 'test1.txt')

重命名

os.rename('test1.txt', 'test.py')

删除

os.remove('test.py')
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值