知识来源:runoob
1、python用法
1.1 标识符
1)第一个字符必须是字母表中字母或下划线 _ 。
2)标识符的其他的部分由字母、数字和下划线组成。
3)标识符对大小写敏感。
1.2 保留字
>>> import keyword >>> keyword.kwlist ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] |
1.3 标准数据类型 (序列操作包括索引,切片,加,乘,检查成员)
不可变:Number、String、Tuple (元组);
可变:List [列表]、Dictionary {字典}、Set {集合}。
1.4 运算符
and |
布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 |
类似java的&& |
or |
布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值。 |
类似java的|| |
not |
布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 |
类似java的! |
is |
is 是判断两个标识符是不是引用自一个对象 |
x is y, 类似 id(x) == id(y) |
is not |
is not 是判断两个标识符是不是引用自不同对象 |
x is not y , 类似 id(a) != id(b) |
1.5 条件控制 (在Python中没有switch – case语句)
if condition_1: statement_block_1 elif condition_2: statement_block_2 else: statement_block_3 |
1.6 循环语句
1)指定区间的值和步长,以会生成数列,for i in range(0, 10, 3):
2)遍历一个序列的索引,for i in range(len(<sequence>)):
3)pass是空语句,是为了保持程序结构的完整性。
while <expr>: <statement(s)> else: <additional_statement(s)>
for <variable> in <sequence>: <statements> else: <statements> |
1.7 迭代器
1)创建一个迭代器:
__iter__() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__() 方法并通过 StopIteration 异常标识迭代的完成。
__next__() 方法(Python 2 里是 next())会返回下一个迭代器对象。
2)使用了 yield 的函数被称为生成器(generator),其返回的是一个迭代器对象:
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
1.8 函数
def functionname(formal_args [,var_arg_default = 35] [,*var_args_tuple], [**var_args_dict]): "函数_文档字符串" function_suite return [expression] |
1)必需参数:(必须传入一个参数,不然会出现语法错误)
2)关键字参数:调用时:functionname(formal_args = 35 )
3)默认参数:
4)不定长参数:
加了星号 * 的参数会以元组(tuple)的形式导入
加了两个星号 ** 的参数会以字典的形式导入
匿名函数:
lambda [arg1 [,arg2,.....argn]]:expression #例子 sum = lambda arg1, arg2: arg1 + arg2 # 调用sum函数 print ("相加后的值为 : ", sum( 10, 20 )) |
1.9 推导式 (列表、字典、集合)
[<expression with variable> for <variable> in <sequence> [condition]] #例子 >>> [[x, x**2] for x in [2, 4, 6] if x > 3] [[4, 16], [6, 36]] >>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],] >>> [[row[i] for row in matrix] for i in range(4)] [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] |
1.10 del 语句
使用 del 语句可以从一个列表中依索引而不是值来删除一个元素。这与使用 pop() 返回一个值不同。可以用 del 语句从列表中删除一个切割,或清空整个列表
1.11 字典创建
>>> {'jack': 4098, 'sape': 4139} >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) >>> {x: x**2 for x in (2, 4, 6)} >>> dict(sape=4139, guido=4127, jack=4098) |
1.12 遍历技巧
1)在字典中遍历时,关键字和对应的值可以使用 items() 方法同时解读出来:
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} >>> for k, v in knights.items(): ... print(k, v) |
2)在序列中遍历时,索引位置和对应值可以使用 enumerate() 函数同时得到:
>>> for i, v in enumerate(['tic', 'tac', 'toe']): ... print(i, v) |
3)同时遍历两个或更多的序列,可以使用 zip() 组合:
注意:
>>> questions = ['name', 'quest', 'favorite color'] >>> answers = ['lancelot', 'the holy grail', 'blue'] >>> for q, a in zip(questions, answers): ... print('What is your {0}? It is {1}.'.format(q, a)) |
1.13 模块
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。
每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。
包是一种管理 Python 模块命名空间的形式,采用"点模块名称"。目录只有包含一个叫做 __init__.py 的文件才会被认作是一个包。
无论是隐式的还是显式的相对导入都是从当前模块开始的。主模块的名字永远是"__main__",一个Python应用程序的主模块,应当总是使用绝对路径引用。
1.14 输入和输出
str.format():格式化输出值
str(): 函数返回一个用户易读的表达形式
repr(): 产生一个解释器易读的表达形式
1.15 错误和异常
1)用户自定义异常:当创建一个模块有可能抛出多种不同的异常时,一种通常的做法是为这个包建立一个基础异常类,然后基于这个基础类为不同的错误情况创建不同的子类。
>>>class MyError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) |
2)预定义的清理行为:关键词 with 语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法
with open("myfile.txt") as f: for line in f: print(line, end="") |
3) 异常处理
try: <expression> except IOError: <expression> raise else: <expression> finally: <expression> |
1.16 面向对象
1)类的方法:
在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数。
self 代表的是类的实例,代表当前对象的地址。
self 的名字并不是规定死的,也可以使用 this,但是最好还是按照约定是用 self。
2)类的私有属性:
两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
3)类的私有方法:
两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。self.__private_methods。
4)继承 class DerivedClassName(modname.BaseClassName):
5)多继承 class DerivedClassName(Base1, Base2, Base3):
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
6)方法重写:
c = Child() # 子类实例 c.myMethod() # 子类调用重写方法 super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法 |
1.17 命名空间和作用域
1)命名空间 :(命名空间查找顺序为:局部-> 全局-> 内置,命名空间的生命周期取决于对象的作用域)
①、内置名称, Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
②、全局名称,模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
③、局部名称,函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
2) 作用域:
Python 中只有模块,类以及函数、lambda才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)则不会,因此这些代码块内定义的变量,外部也可以访问。
B(Built-in): 包含了内建的变量/关键字等
G(Global):当前脚本的最外层,比如当前模块的全局变量。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。
L(Local):最内层,包含局部变量,比如一个函数/方法内部。
①、局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
total = 0 # 这是一个全局变量 def sum( arg1, arg2 ): total = arg1 + arg2 # total在这里是局部变量. return total |
②、当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字
num = 1 def fun1(): global num # 需要使用 global 关键字声明 num = 123
def outer(): num = 10 def inner(): nonlocal num # nonlocal关键字声明 num = 100 inner() |
1.18 补充
在交互模式中,最后被输出的表达式结果被赋值给变量 _,此处, _ 变量应被用户视为只读变量。
在整数除法中,除法 / 总是返回一个浮点数,如果只想得到整数的结果,丢弃可能的分数部分,可以使用运算符 // ,但是// 得到的并不一定是整数类型的数,它与分母分子的数据类型有关系。
type()不会认为子类是一种父类类型,isinstance()会认为子类是一种父类类型。
创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
2、用type动态生成类的方式
#type(类名,由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(属性名称: 属性值, 方法名: 已声明的函数名)) args=13 def fn(self, name='world'): print('Hello,%s' % name) Hello =type('Hello', (object,), dict(hello=fn,age=args)) #所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。 >>> args.__class__.__class__ <type 'type'> >>> fn.__class__.__class__ <type 'type'> |
3、python gil
GIL:即全局解释器所(global interpreter lock),每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说多线程并不是真正意义上的同时执行。
python程序 -> cpython解析器(会上gil锁,保证同时只有一个线程占用cpu) -> [CPU执行]
3.1 如何解决GIL锁的问题呢?
1)更换cpython为jpython(不建议)
2)使用多进程完成多线程的任务
3)在使用多线程可以使用c语言去实现
3.2 什么时候会释放Gil锁
1)非公平竞争:遇到像 i/o操作这种会有时间空闲情况造成cpu闲置的情况,会释放Gil锁。(释放锁的进程不参加竞争)
2)公平竞争:会有一个专门ticks进行计数一旦ticks数值达到100 的时候,会释放Gil锁。(线程之间开始竞争Gil锁)
3.3 互斥锁和Gil锁的关系
Gil锁 : 保证同一时刻只有一个线程能使用到cpu
互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱
首先假设只有一个进程,这个进程中有两个线程 Thread1,Thread2, 要修改共享的数据date, 并且有互斥锁,执行以下步骤: (1)多线程运行,假设Thread1获得GIL可以使用cpu,这时Thread1获得 互斥锁lock,Thread1可以改date数据(但并没有开始修改数据); (2)Thread1线程在修改date数据前发生了 i/o操作 或者 ticks计数满100 (注意就是没有运行到修改data数据),这个时候 Thread1 让出了Gil,Gil锁可以被竞争; (3) Thread1 和 Thread2 开始竞争 Gil (注意:如果Thread1是因为 i/o 阻塞 让出的Gil Thread2必定拿到Gil,如果Thread1是因为ticks计数满100让出Gil 这个时候 Thread1 和 Thread2 公平竞争); (4)假设 Thread2正好获得了GIL, 运行代码去修改共享数据date,由于Thread1有互斥锁lock,所以Thread2无法更改共享数据date,这时Thread2让出Gil锁 , GIL锁再次发生竞争 ; (5)假设Thread1又抢到GIL,由于其有互斥锁Lock所以其可以继续修改共享数据data,当Thread1修改完数据释放互斥锁lock,Thread2在获得GIL与lock后才可对data进行修改。 |