文章目录
- 创建只有一个值的 tuple
- 不可变对象调用方法
- 使用python的下标循环
- 全排列
- python中的三目运算符
- 生成器
- 可迭代对象和迭代器
- 迭代器与生成器的区别
- 函数名是一个变量
- 高阶函数
- 闭包
- nonlocal与global
- 偏函数
- 对象可绑定任何数据
- 类的访问控制
- 多态
- MixIn
- 关于对象的属性方法优先级问题
- 定制类
- 把方法变成属性调用
- @property
- @xxx.setter
- try...except可跨越多层调用
- 正则表达式
- (?:···)
- r + 字符串
- 匹配规则
- 文件读写模式
- 合并拆分路径
- 复制、重命名、删除文件
这是我在 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')
更多推荐
廖雪峰Python教程笔记
发布评论