- 生成式 生成器使用
import sys f = [x for x in range(1, 10)] print(f) f = [x + y for x in 'ABCDE' for y in '1234567'] print(f) # 用列表的生成表达式语法创建列表容器 # 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间 f = [x ** 2 for x in range(1, 1000)] print(sys.getsizeof(f)) # 查看对象占用内存的字节数 print(f) # 请注意下面的代码创建的不是一个列表而是一个生成器对象 # 通过生成器可以获取到数据但它不占用额外的空间存储数据 # 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间) f = (x ** 2 for x in range(1, 1000)) print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间 print(f) for val in f: print(val)
迭代 迭代器 生成器
-
迭代
-
迭代器
-
生成器
-
yield 语法
- 如果一个函数定义中包含 yield 表达式,那么该函数是一个生成器函数(而非普通函数)
- 实际上 yield 只能用于定义生成器 函数
- 生成器函数被调用后,其函数体内的代码并不会立即执行,而是返回一个生成器(generator-iterator)。当返回的生成器调用成员方法时,相应的生成器函数中的代码才会执行
- 每次从暂停恢复时,生成器函数的内部变量、指令指针、内部求值栈等内容和暂停时完全一致。
- 调用带有yield 的函数,不会立即执行函数,而是返回一个 iterable 对象
一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
-
yield from
- yield from iterable 本质上就是一个简短的形式:for item in iterable: yield item
元组
- 元组的操作
# 定义元组 t = ('骆昊', 38, True, '四川成都') print(t) # 获取元组中的元素 print(t[0]) print(t[3]) # 遍历元组中的值 for member in t: print(member) # 重新给元组赋值 # t[0] = '王大锤' # TypeError # 变量t重新引用了新的元组原来的元组将被垃圾回收 t = ('王大锤', 20, True, '云南昆明') print(t) # 将元组转换成列表 person = list(t) print(person) # 列表是可以修改它的元素的 person[0] = '李小龙' person[1] = 25 print(person) # 将列表转换成元组 fruits_list = ['apple', 'banana', 'orange'] fruits_tuple = tuple(fruits_list) print(fruits_tuple)
- 说明:
- 元组中的元素是无法修改的:
- 如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,
- 如果一个方法要返回多个值,使用元组也是不错的选择。
- 元组在创建时间和占用的空间上面都优于列表:我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间
- 元组中的元素是无法修改的:
集合
集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集等运算
- 集合创建和使用
# 创建集合的字面量语法 set1 = {1, 2, 3, 3, 3, 2} print(set1) print('Length =', len(set1)) # 创建集合的构造器语法(面向对象部分会进行详细讲解) set2 = set(range(1, 10)) set3 = set((1, 2, 3, 3, 2, 1)) print(set2, set3) # 创建集合的推导式语法(推导式也可以用于推导集合) set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0} print(set4)
- 集合添加和删除元素
set1.add(4) set1.add(5) set2.update([11, 12]) set2.discard(5) if 4 in set2: set2.remove(4) print(set1, set2) print(set3.pop()) print(set3)
- 集合 交集、并集、差集等运算
# 集合的交集、并集、差集、对称差运算 print(set1 & set2) # print(set1.intersection(set2)) print(set1 | set2) # print(set1.union(set2)) print(set1 - set2) # print(set1.difference(set2)) print(set1 ^ set2) # print(set1.symmetric_difference(set2)) # 判断子集和超集 print(set2 <= set1) # print(set2.issubset(set1)) print(set3 <= set1) # print(set3.issubset(set1)) print(set1 >= set2) # print(set1.issuperset(set2)) print(set1 >= set3) # print(set1.issuperset(set3))
字典
字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开
- 字典使用
# 创建字典的字面量语法 scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82} print(scores) # 创建字典的构造器语法 items1 = dict(one=1, two=2, three=3, four=4) # 通过zip函数将两个序列压成字典 items2 = dict(zip(['a', 'b', 'c'], '123')) # 创建字典的推导式语法 items3 = {num: num ** 2 for num in range(1, 10)} print(items1, items2, items3) # 通过键可以获取字典中对应的值 print(scores['骆昊']) print(scores['狄仁杰']) # 对字典中所有键值对进行遍历 for key in scores: print(f'{key}: {scores[key]}') # 更新字典中的元素 scores['白元芳'] = 65 scores['诸葛王朗'] = 71 scores.update(冷面=67, 方启鹤=85) print(scores) if '武则天' in scores: print(scores['武则天']) print(scores.get('武则天')) # get方法也是通过键获取对应的值但是可以设置默认值 print(scores.get('武则天', 60)) # 删除字典中的元素 print(scores.popitem()) print(scores.popitem()) print(scores.pop('骆昊', 100)) # 清空字典 scores.clear() print(scores)
面向对象
-
类的定义:
class student(object): # __init__是一个特殊方法用于在创建对象时进行初始化操作 # 通过这个方法我们可以为学生对象绑定name和age两个属性 def __init__(self, name, message): self.name = name self.message = message def study(self, course_name): print('%s正在学习%s.' % (self.name, course_name)) # PEP 8要求标识符的名字用全小写多个单词用下划线连接 # 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识) def watch_movie(self): if self.age < 18: print('%s只能观看《熊出没》.' % self.name) else: print('%s正在观看岛国爱情大电影.' % self.name)
-
对象的创建 和使用
def main(): # 创建学生对象并指定姓名和年龄 stu1 = Student('骆昊', 38) # 给对象发study消息 stu1.study('Python程序设计') # 给对象发watch_av消息 stu1.watch_movie() stu2 = Student('王大锤', 15) stu2.study('思想品德') stu2.watch_movie()
if name == ‘main’:
main() -
访问可见性问题
- 私有属性:用两个下滑线开头(实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问)
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))
注:实例变量.__name和实例变量.__score 外部无法访问
- 一个下划线开头的实例变量名:
_name
实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”
不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量
-
继承 和多态
- 继承写法
class Dog(Animal):
其中Animal 为父类 - 判断一个变量是否是某个类型可以用
isinstance()
判断: eg:isinstance(a, list)
- 继承写法
-
获取对象信息
- 判断对象类型:
- 使用
type()
eg :type(fn)==types.FunctionType
- 使用
isinstance()
(总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。)
eg:isinstance(h, Object)
isinstance([1, 2, 3], (list, tuple))
- 使用
dir()
(获得一个对象的所有属性和方法)
eg:dir('ABC') # ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
类似__xxx__的属性和方法在Python中都是有特殊用途
如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法
len('ABC')
和'ABC'.__len__()
等价
- 使用
- 判断对象类型:
-
实例属性和类属性
-
实例属性属于各个实例所有,互不干扰;
-
类属性属于类所有,所有实例共享一个属性;
-
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误
-
面向对象高级特性
-
使用
__slots__
限制实例的属性
eg : 只允许 Student 添加name
和age
属性class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
-
使用
@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 if __name__ =="__main__": s = Student() s.score = 60 # OK,实际转化为s.set_score(60) s.score # OK,实际转化为s.get_score()
-
多重继承
- MixIn就是一种常见的设计
# 为了更好地看出继承关系,我们把Runnable和Flyable改为RunnableMixIn和FlyableMixIn class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
-
定制类
-
__sorts__
: 初始化属性 -
__len__()
求长度 -
__str__
: 返回一个定制化的String -
__repr__()
:返回程序调用的字符串 --> 和__str__
两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的 -
__iter__
__next__
: 如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__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__
: 把对象看成list
或者dic
来获取值 依据参数获取属性值class Fib(object): def __getitem__(self, n): # _getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断 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
-
__setitem__()
方法,把对象视作list、tuple 或dict来对集合赋值 -
__delitem__()
删除值 -
__getattr__
动态返回属性class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
-
__call__
只需要定义一个__call__()方法,就可以直接对实例进行调用class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
注:
*__call__()
还可以定义参数
* 通过callable()函数,我们就可以判断一个对象是否是“可调用”对象 eg:callable(Student())
-
枚举类
- eg:
from enum import Enum # alue属性则是自动赋给成员的int常量,默认从1开始计数 Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
- 如果需要更精确地控制枚举类型,可以从Enum派生出自定义类
from enum import Enum, unique # @unique装饰器可以帮助我们检查保证没有重复值 @unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6
- 枚举类的访问
day1 = Weekday.Mon Weekday['Tue'] Weekday.Tue.value Weekday(1)
元类使用
动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。
-
type()
-
type()函数可以查看一个类型或变量的类型
-
type()函数既可以返回一个对象的类型,又可以创建出新的类型
eg:我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)…的定义:def fn(self, name='world'): print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
-
要创建一个class对象,type()函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
-
-
metaclass
-
metaclass,直译为元类,简单的解释就是:
- 当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
- 但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
- 连接起来就是:先定义metaclass,就可以创建类,最后创建实例。
- metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”
-
eg:
# metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs)
-
Python解释器在创建MyList时:
ListMetaclass.__new__()
-
__new__()
方法接收到的参数依次是:-
当前准备创建的类的对象;
-
类的名字;
-
类继承的父类集合;
-
类的方法集合。
-
-
模块、包
- 在python 中,一个 .py 文件就是一个模块module
- 自己创建的模块,不能和python自带的模块冲突,否则,无法导入系统自带的模块。
- python又引入了按目录来组织模块的方法,称为包(Package)(文件夹)
- 每一个包目录下面都会有一个
__init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。
- 每一个包目录下面都会有一个
模块使用:
模块作用域
- 私有的
private
:_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等
第三方模块:
- sys 模块
- sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称