-
Python的元组与列表类似,不同之处在于元组的元素不能修改,元组使用小括号,列表使用方括号,元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可一、创建元组复制代码代码如下:tup1 = ('physics', 'chemistry', 1997, 2000);tup2 = (1, 2, 3, 4, 5 );tup3 = "a", "b", "c", "d";创建空元组复制代码代码如下:tup1 = ();元组中只包含一个元素时,需要在元素后面添加逗号来消除歧义复制代码代码如下:tup1 = (50,);元组与字符串类似,下标索引从0开始,可以进行截取,组合等。二、访问元组元组可以使用下标索引来访问元组中的值,如下实例:复制代码代码如下:#!/usr/bin/pythontup1 = ('physics', 'chemistry', 1997, 2000);tup2 = (1, 2, 3, 4, 5, 6, 7 );print "tup1[0]: ", tup1[0]print "tup2[1:5]: ", tup2[1:5]#以上实例输出结果:#tup1[0]: physics#tup2[1:5]: [2, 3, 4, 5]三、修改元组元组中的元素值是不允许修改的,但我们可以对元组进行连接组合,如下实例:复制代码代码如下:#!/usr/bin/pythontup1 = (12, 34.56);tup2 = ('abc', 'xyz');# 以下修改元组元素操作是非法的。# tup1[0] = 100;# 创建一个新的元组tup3 = tup1 + tup2;print tup3;#以上实例输出结果:#(12, 34.56, 'abc', 'xyz')四、删除元组元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组,如下实例:复制代码代码如下:#!/usr/bin/pythontup = ('physics', 'chemistry', 1997, 2000);print tup;del tup;print "After deleting tup : "print tup;#以上实例元组被删除后,输出变量会有异常信息,输出如下所示:#('physics', 'chemistry', 1997, 2000)#After deleting tup :#Traceback (most recent call last):# File "test.py", line 9, in# print tup;#NameError: name 'tup' is not defined[/code]五、元组运算符与字符串一样,元组之间可以使用 + 号和 * 号进行运算。这就意味着他们可以组合和复制,运算后会生成一个新的元组。六、元组索引,截取因为元组也是一个序列,所以我们可以访问元组中的指定位置的元素,也可以截取索引中的一段元素,如下所示:元组:复制代码代码如下:L = ('spam', 'Spam', 'SPAM!')七、无关闭分隔符任意无符号的对象,以逗号隔开,默认为元组,如下实例:代码如下:#!/usr/bin/pythonprint 'abc', -4.24e93, 18+6.6j, 'xyz';x, y = 1, 2;print "Value of x , y : ", x,y;以上实例允许结果:复制代码代码如下:abc -4.24e+93 (18+6.6j) xyzValue of x , y : 1 2八、元组内置函数Python元组包含了以下内置函数1、cmp(tuple1, tuple2):比较两个元组元素。2、len(tuple):计算元组元素个数。3、max(tuple):返回元组中元素最大值。4、min(tuple):返回元组中元素最小值。5、tuple(seq):将列表转换为元组。九、另一种解读tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出同学的名字:复制代码代码如下:>>> classmates = ('Michael', 'Bob', 'Tracy')现在,classmates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素。不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。tuple的陷阱:当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来,比如:复制代码代码如下:>>> t = (1, 2)>>> t(1, 2)如果要定义一个空的tuple,可以写成():复制代码代码如下:>>> t = ()>>> t()但是,要定义一个只有1个元素的tuple,如果你这么定义:复制代码代码如下:>>> t = (1)>>> t1定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。所以,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义:复制代码代码如下:>>> t = (1,)>>> t(1,)Python在显示只有1个元素的tuple时,也会加一个逗号,,以免你误解成数学计算意义上的括号。在来看一个“可变的”tuple:复制代码代码如下:>>> t = ('a', 'b', ['A', 'B'])>>> t[2][0] = 'X'>>> t[2][1] = 'Y'>>> t('a', 'b', ['X', 'Y'])这个tuple定义的时候有3个元素,分别是'a','b'和一个list。不是说tuple一旦定义后就不可变了吗?怎么后来又变了?别急,我们先看看定义的时候tuple包含的3个元素:当我们把list的元素'A'和'B'修改为'X'和'Y'后,tuple变为:表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!理解了“指向不变”后,要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。
-
一、什么是恺撒密码恺撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法,它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符:原文:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z密文:D E F G H I J K L M N O P Q R S T U V W X Y Z A B C原文字符P,其密文字符C满足如下条件:C = ( P + 3 ) mod 26解密方法反之,满足:P = ( C – 3 ) mod 26二、程序运行环境程序运行环境是:pycharm2021三、恺撒密码:加密恺撒密码的加密算法程序首先接收用户输入的文本然后对字母a-z和字母A-Z按照密码算法进行转换3.1 恺撒密码加密实例程序# 恺撒密码加密def Caesar_PW_Encryption(): inputText = input("请输入明文文本: ") for index in inputText: if "a" <= index <= "z": print(chr(ord("a") + (ord(index) - ord("a") + 3) % 26), end='') elif "A" <= index <= "Z": print(chr(ord("A") + (ord(index) - ord("A") + 3) % 26), end='') else: print(index, end='')在主函数中调用这个Caesar_PW_Encryption恺撒密码加密函数,如下所示123if __name__ == '__main__': # 恺撒密码加密 Caesar_PW_Encryption()3.2 恺撒密码加密实例程序运行结果四、恺撒密码:解密恺撒密码的解密算法程序首先接收用户输入的加密文本然后对字母a-z和字 母A-Z按照密 码算法进行反向转换4.1 恺撒密码解密实例程序# 恺撒密码解密def Ceasar_PW_Decryption(): inputText = input("请输入加密后文本: ") for index in inputText: if "a" <= index <= "z": print(chr(ord("a") + (ord(index) - ord("a") - 3) % 26), end='') elif "A" <= index <= "Z": print(chr(ord("A") + (ord(index) - ord("A") - 3) % 26), end='') else: print(index, end='')在主函数中调用这个Caesar_PW_Encryption恺撒密码加密函数,如下所示123456if __name__ == '__main__': # 恺撒密码加密 Caesar_PW_Encryption() # 恺撒密码解密 Ceasar_PW_Decryption()4.2 恺撒密码解密实例程序运行结果
-
虽然效率上还有点问题,但是功能上从逻辑上是已经完成了的。 限于本人水平有限,如果有不合理之处,你也别bb,因为谁也不比谁强多少。 认真的讲,可以揭示许多黑盒的本质问题,而不是对着CPU说明书照本学科。D_BIT_ZERO = '0' D_BIT_ONE = '1' D_TRUE = D_BIT_ONE D_FALSE = D_BIT_ZERO D_TEN_ONE = '1' D_TEN_TWO = '2' D_TEN_THREE = '3' D_TEN_FOUR = '4' D_TEN_FIVE = '5' D_TEN_SIX = '6' D_TEN_SEVEN = '7' D_TEN_EIGHT = '8' D_TEN_NINE = '9' D_TEN_ZERO = '0' D_ONE = '00000001' D_TWO = '00000010' D_THREE = '00000011' D_FOUR = '00000100' D_FIVE = '00000101' D_SIX = '00000110' D_SEVEN = '00000111' D_EIGHT = '00001000' D_NINE = '00001001' D_ZERO = '00000000' D_TEN = '00001010' D_LENGTH = D_EIGHT D_BLANK = '' def d_bit_is(a, b): """位操作-是""" if a == D_BIT_ZERO: if b == D_BIT_ZERO: return D_TRUE if b == D_BIT_ONE: return D_FALSE if a == D_BIT_ONE: if b == D_BIT_ZERO: return D_FALSE if b == D_BIT_ONE: return D_TRUE def d_bit_isnt(a, b): """位操作-非""" return d_bit_not(d_bit_is(a, b)) def d_bit_and(a, b): """位操作-与""" if a == D_BIT_ZERO: if b == D_BIT_ZERO: return D_FALSE if b == D_BIT_ONE: return D_FALSE if a == D_BIT_ONE: if b == D_BIT_ZERO: return D_FALSE if b == D_BIT_ONE: return D_TRUE def d_bit_not(a): """位操作-取反""" if a == D_BIT_ZERO: return D_BIT_ONE if a == D_BIT_ONE: return D_BIT_ZERO def d_bit_or(a, b): """位操作-或""" if a == D_BIT_ZERO: if b == D_BIT_ZERO: return D_FALSE if b == D_BIT_ONE: return D_TRUE if a == D_BIT_ONE: if b == D_BIT_ZERO: return D_TRUE if b == D_BIT_ONE: return D_TRUE def d_bit_xor(a, b): """ 位操作-异或 简单的说,位的异或结果就是相同为0,不同为1 """ return d_bit_or(d_bit_and(d_bit_not(a), b), d_bit_and(a, d_bit_not(b))) def d_xor(m, n): res = '' ml = list(m) nl = list(n) while ml and nl: mlp = ml.pop() nlp = nl.pop() xor = d_bit_xor(mlp, nlp) res = xor + res return res def d_is(m, n): """ 判断两个数相等 数的表示和值是满射,直接复用字符串的比较方法,提升效率 下面段1使用栈的方式更接近本质一点 段2无法避开index的数值运算 段3是异或串联的思路,只要有一位是不同的,串联结果即不同 段4的并行迭代貌似不是扁平了,暂时不讨论了 """ if m == n: return D_TRUE else: return D_FALSE # 段1 # res = D_TRUE # ml = list(m) # nl = list(n) # while ml: # mlp = ml.pop() # nlp = nl.pop() # if d_bit_isnt(mlp, nlp) == D_TRUE: # res = D_FALSE # break # return res # 段2 # res = D_TRUE # i = D_ZERO # # i in range(d_len(num1)) # while D_TRUE == D_TRUE: # if d_not(d_bit_and(d_less_or_is(D_ZERO, i), d_less(i, d_len(num1)))) == D_TRUE: # break # # b1 = num1[i] # b1 = d_index(num1, i) # # b2 = num2[i] # b2 = d_index(num2, i) # if d_bit_isnt(b1, b2) == D_TRUE: # res = D_FALSE # break # i = d_next(i) # 段3 # xor = d_xor(num1, num2) # # 当且仅当xor各位同为0时两数相等 # s_xor = d_serials_or(xor) # if s_xor == D_FALSE: # return D_TRUE # 段4 # for m, n in zip(num1, num2): # if d_bit_is(m, n) == D_FALSE: # return D_FALSE def d_s_and(num): """ 串联与 """ res = D_TRUE for n in num: res = d_bit_and(res, n) return res def d_serials_or(num): """ 串联或 """ res = D_FALSE for n in num: res = d_bit_or(res, n) return res def d_next(num): """ 位操作-后继 注意注释的代码,这里next调用了index,而index又调用了next,这是一个循环算法,导致栈溢出 TODO 自增溢出问题 """ res = '' num_l = list(num) carry = D_TRUE while num_l: b = num_l.pop() if carry == D_TRUE: # 吞掉进位标识 if d_bit_is(b, D_BIT_ZERO) == D_TRUE: carry = D_FALSE # 0变1,1变零(进位自旋) b = d_bit_not(b) res = b + res return res # 失败尝试1 # res = d_xor(num, D_ONE) # return res # 失败尝试2 # # index为-1即length-1 # # res = num[-1] # res = d_index(num, d_prev(d_len(num))) # # 进位标识 # b_carry = D_FALSE # # 如果最低位为1 # if d_bit_is(res, D_BIT_ONE) == D_TRUE: # # 结果最低位置0 # res = D_BIT_ZERO # # 进位 # b_carry = D_BIT_ONE # # 如果最低位为0 # if d_bit_is(res, D_BIT_ZERO) == D_TRUE: # # 结果最低位置1 # res = D_BIT_ONE # # i in range(1, len(num)) # i = D_ONE # while d_bit_and(d_less_or_is(D_ONE, i), d_less(i, d_len(num))) == D_TRUE: # # 自右往左取第i位 # # index = -(i+1) 即 length-1-i # index = d_minus(d_minus(d_len(num), i), D_ONE) # # bi = num[-i - 1] # -2,-3,...,-8 # bi = d_index(num, index) # # 如果第i位为0,且前i-1位进位的情况 # if d_bit_and(d_bit_is(bi, D_BIT_ZERO), d_bit_is(b_carry, D_BIT_ONE)) == D_TRUE: # # 本位置1 # bi = D_BIT_ONE # # 进位标识置0 # b_carry = D_BIT_ZERO # # 低位全置0 TODO # res = d_zero(d_len(res)) # # 如果第i位为1,且前i-1位进位的情况 # if d_bit_and(d_bit_is(bi, D_BIT_ONE), d_bit_is(b_carry, D_BIT_ONE)) == D_TRUE: # # 本位置0,继续进位 # bi = D_BIT_ZERO # res = bi + res # i = d_next(i) # # 溢出 # if b_carry == D_TRUE: # res = d_zero(d_len(res)) # return res def d_bit_less(a, b): """位操作-小于""" if d_bit_is(a, D_BIT_ONE) == D_TRUE: if d_bit_is(b, D_BIT_ZERO) == D_TRUE: return D_FALSE if d_bit_is(a, D_BIT_ZERO) == D_TRUE: if d_bit_is(b, D_BIT_ONE) == D_TRUE: return D_TRUE def d_less_compare(m, n): res = D_FALSE ml = list(m) nl = list(n) # TODO and # TODO 对于等于的讨论 while ml: mlp = ml.pop() nlp = nl.pop() if d_bit_less(mlp, nlp) == D_TRUE: res = D_TRUE if d_bit_less(nlp, mlp) == D_TRUE: res = D_FALSE # TODO 关于负数的特殊讨论 return res def d_less(m, n): if m == d_bound(d_len(m)): # TODO m = d_zero(d_len(m)) # TODO # 先进行有效位数的比较提高效率 m_len = d_len(d_cut_left_zeros(m)) n_len = d_len(d_cut_left_zeros(n)) m_len, n_len = d_alin(m_len, n_len) if d_less_compare(m_len, n_len) == D_TRUE: return D_TRUE return d_less_compare(m, n) def d_less_or_is(num1, num2): """小于等于""" dp = d_less(num1, num2) di = d_is(num1, num2) return d_bit_or(dp, di) def d_plus(m, n): """加法""" # 比较交换,保证左侧是大数 if d_less(m, n) == D_TRUE: m, n = n, m res = m # 溢出标识 of = D_FALSE zero = d_zero(d_len(m)) count = zero while d_less(count, n) == D_TRUE: # 溢出之后,res会归零重新处理 res = d_next(res) if d_bit_is(res, zero): of = D_TRUE # 即使溢出,也继续运算,方便后续处理 count = d_next(count) return res, of def d_zero(n=D_LENGTH): """返回n位的0""" # 8位直接返回常量 if d_is(n, D_EIGHT) == D_TRUE: return D_ZERO # 其他位数直接拼接 # TODO 关于异常输入的判断问题,何时抛异常,何时逻辑处理 else: res = D_BLANK i = D_ZERO while D_TRUE == D_TRUE: if d_is(i, n) == D_TRUE: break # TODO += res = res + D_BIT_ZERO i = d_next(i) return res def d_bound(n): """返回n位的越界数""" return d_ones(n) def d_one(n=D_LENGTH): """返回8位的1""" # TODO 校验方法调用 if d_is(n, D_EIGHT) == D_TRUE: return D_ONE else: res = D_ONE i = D_ZERO # 循环 n-1 次 while D_TRUE == D_TRUE: if d_is(i, n) == D_TRUE: break res = D_ZERO + res i = d_next(i) return res def d_ones(n=D_LENGTH): """返回8位全1""" return d_not(d_zero(n)) def d_one_neg(n=D_LENGTH): """返回负1""" # TODO 校验方法调用 return d_not(d_one(n)) def d_mul(m, n): """ 乘法 效率: 1.比较交换 """ if d_less(m, n): m, n = n, m res = m # 溢出标识 of = D_FALSE # 乘法扩容问题 i = d_next(d_zero(d_len(m))) while D_TRUE == D_TRUE: # jump = d_bit_not(d_less(i, n)) if d_is(i, n) == D_TRUE: break res, off = d_plus(res, m) i = d_next(i) # 越界 if off == D_TRUE: of = D_TRUE # TODO 溢出问题 return res, of def d_power(m, n): """幂""" res = m if d_is(n, d_zero(d_len(m))) == D_TRUE: return d_one(d_len(m)) count = d_one() # 执行n-1次 while D_TRUE == D_TRUE: if d_is(count, n) == D_TRUE: break res, _ = d_mul(res, m) count = d_next(count) return res def d_not(num): """并列取反""" res = D_BLANK for n in num: res = res + d_bit_not(n) return res def d_prev(num): """前一元素""" # one_neg = d_ones_neg() # res, _ = d_plus(num, one_neg) res = D_BLANK # 借位标识 borrow = D_TRUE nl = list(num) while nl: b = nl.pop() if borrow == D_TRUE: if d_bit_is(b, D_BIT_ONE) == D_TRUE: # 共轭 borrow = D_FALSE # 自旋 b = d_bit_not(b) res = b + res return res # # # # i in range(0, d_len(num)) # while d_bit_and(d_less_or_is(D_ZERO, i), d_less(i, d_len(num))) == D_TRUE: # # 自右往左取第i位 # index = d_minus(d_minus(d_len(num), D_ONE), i) # # bi = num[-(i + 1)] # -1, -2,-3,...,-8 # bi = d_index(num, index) # # 如果第i位为1,且前i-1位借位的情况 # if d_bit_and(d_bit_is(bi, D_BIT_ONE), borrow) == D_TRUE: # # 本位置0,借位标识置0,后续位数均置1 # bi = D_BIT_ZERO # borrow = D_FALSE # res = d_ones(d_len(res)) # res = bi + res # i = d_next(i) # # 溢出 # if borrow == D_TRUE: # res = d_zero(d_len(res)) # return res def d_minus(m, n): """减法""" res = m i = d_zero(d_len(m)) # 执行n次 while D_TRUE == D_TRUE: if d_is(i, n) == D_TRUE: break res = d_prev(res) i = d_next(i) # 对于结果为0的特殊处理(存在两个零的问题) TODO 待完善 if d_is(res, d_bound(d_len(n))) == D_TRUE: res = d_zero(d_len(m)) return res def d_right(num): """右移""" res = '0' i = D_ZERO # i in range(d_len(num) - 1) while D_TRUE == D_TRUE: if d_is(i, d_prev(d_len(num))) == D_TRUE: break res = res + d_index(num, i) i = d_next(i) return res def d_left(num): """左移""" res = '0' i = D_ZERO # i in range(d_len(num) - 1) while D_TRUE == D_TRUE: if d_is(i, d_prev(d_len(num))) == D_TRUE: break index = d_minus(d_minus(d_len(num), D_ONE), i) # res = num[-i - 1] + res res = d_index(num, index) + res i = d_next(i) return res def d_frac(m, n): """除法""" # 如: 127/10 127-10=117 if d_less(m, n) == D_TRUE: return d_zero(d_len(m)), m remainder = m quotient = d_zero(d_len(m)) while True: remainder = d_minus(remainder, n) quotient = d_next(quotient) if d_less(remainder, n) == D_TRUE: break return quotient, remainder def d_bin_exp(num): """整数的二进制表示""" exp = D_ZERO i = D_ZERO # i in range(d_len(num)): dlen = d_len(num) while D_TRUE == D_TRUE: if d_is(i, dlen) == D_TRUE: break index = d_minus(d_minus(dlen, D_ONE), i) # b = num[-i-1] b = d_index(num, index) bb = d_ten_to_bin(b) m, _ = d_mul(bb, d_power(D_TEN, i)) exp, _ = d_plus(exp, m) i = d_next(i) return exp def d_ten_exp(num): """ 整数的十进制表示 循环商10取余 以123为例说明: 123/10 = 12...3 12/10 = 1...2 1/10 = 0...1 则3为最低位 2为次位 1为最高位 """ # 以123为例说明: 123/10 = 12...3 12/10 = 1...2 1/10 = 0...1 # 3为最低位 2为次位 1为最高位 exp = D_BLANK while D_TRUE == D_TRUE: num, r = d_frac(num, D_TEN) bt = d_bin_to_ten(r) exp = bt + exp if d_is(D_ZERO, num) == D_TRUE: break if exp == D_BLANK: exp = D_BIT_ZERO return exp def d_cut_right_zeros_align(num): """截掉右侧的零,并对齐""" res = d_cut_right_zeros(num) i = D_ZERO # i in range(d_len(res), d_len(num)) dlen = d_len(num) while D_TRUE == D_TRUE: if d_is(i, dlen) == D_TRUE: break res = D_BIT_ZERO + res i = d_next(i) return res def d_cut_right_zeros(num): """截掉右侧的零""" res = D_BLANK join_flag = D_FALSE i = D_ZERO # i in range(d_len(num)) dlen = d_len(num) while D_TRUE == D_TRUE: if d_is(i, dlen) == D_TRUE: break index = d_minus(d_minus(dlen, D_ONE), i) # b = num[-i-1] b = d_index(num, index) # 出现第一个0后则不再继续截断 if join_flag == D_FALSE: if b == D_BIT_ONE: join_flag = D_TRUE if join_flag == D_TRUE: res = res + b i = d_next(i) return res def d_ten_dec_exp(num): """ 小数部分的十进制表示 放大位数,循环乘二进制的10,截取整数位,直至小数位为0 例如: 0.d1 d2 ... dn = d1*10^-1 + d2*10^-2 + ... + dn*10^-n d1.d2 ... dn = d1 + d2*10^-1 + ... + dn*10^-(n-1) TODO 效率低 """ exp = D_BLANK num_d = num while D_TRUE == D_TRUE: # num_d = 11111110 if d_is(num_d, D_ZERO) == D_TRUE: break num_ex = d_expand_left(num_d) num_mul, _ = d_mul(num_ex, d_expand_left(D_TEN)) num_i, num_d = d_float_bin_parts(num_mul) num_i_ten = d_bin_to_ten(num_i) exp = exp + num_i_ten return exp def d_ten_to_bin(num): """十进制位转为二进制位""" if num == D_TEN_ONE: return D_ONE if num == D_TEN_TWO: return D_TWO if num == D_TEN_THREE: return D_THREE if num == D_TEN_FOUR: return D_FOUR if num == D_TEN_FIVE: return D_FIVE if num == D_TEN_SIX: return D_SIX if num == D_TEN_SEVEN: return D_SEVEN if num == D_TEN_EIGHT: return D_EIGHT if num == D_TEN_NINE: return D_NINE if num == D_TEN_ZERO: return D_ZERO # 表示错误,方便调试,实际应该抛出一个异常 ex = Exception("二进制转换异常!") raise ex def d_bin_to_ten(num): """二进制位转为十进制位""" if d_is(num, D_ONE) == D_TRUE: return D_TEN_ONE if d_is(num, D_TWO) == D_TRUE: return D_TEN_TWO if d_is(num, D_THREE) == D_TRUE: return D_TEN_THREE if d_is(num, D_FOUR) == D_TRUE: return D_TEN_FOUR if d_is(num, D_FIVE) == D_TRUE: return D_TEN_FIVE if d_is(num, D_SIX) == D_TRUE: return D_TEN_SIX if d_is(num, D_SEVEN) == D_TRUE: return D_TEN_SEVEN if d_is(num, D_EIGHT) == D_TRUE: return D_TEN_EIGHT if d_is(num, D_NINE) == D_TRUE: return D_TEN_NINE if d_is(num, D_ZERO) == D_TRUE: return D_TEN_ZERO # 表示错误,方便调试,实际应该抛出一个异常 ex = Exception("十进制转换异常!") raise ex def d_float_ten_exp(num): """浮点数的十进制表示""" i, d = d_float_bin_parts(num) exp_int = d_ten_exp(i) exp_deci = d_ten_dec_exp(d) exp = exp_int + '.' + exp_deci return exp def d_float_bin_exp(num): """浮点数的二进制表示""" dot = '.' dlen = d_len(num) index = d_bin_exp(str(num.index(dot))) # i = num[0:index] i = d_slice(num, D_ZERO, index) # d = num[index + 1:] d = d_slice(num, d_next(index), dlen) exp_i = d_bin_exp(i) exp_d = d_bin_dec_exp(d) return exp_i + exp_d def d_cut_left_zeros(num): res = D_BLANK join_flag = D_FALSE i = D_ZERO # i in range(d_len(num)) dlen = d_len(num) while D_TRUE == D_TRUE: if d_is(i, dlen) == D_TRUE: break # b = num[i] b = d_index(num, i) # 出现第一个0后则不再继续截断 if join_flag == D_FALSE: if b == D_BIT_ONE: join_flag = D_TRUE if join_flag == D_TRUE: res = b + res i = d_next(i) return res def d_cut_left_zeros_align(num): """截掉左侧的零,然后右侧补零""" res = d_cut_left_zeros(num) i = d_len(res) # i in range(d_len(res), d_len(num)) dlen = d_len(num) while D_TRUE == D_TRUE: if d_is(i, dlen) == D_TRUE: break res = res + D_BIT_ZERO i = d_next(i) return res def d_align_right(num): """右侧补零""" # i in range(d_len(num), D_LENGTH) i = d_len(num) while D_TRUE == D_TRUE: if d_is(i, D_LENGTH) == D_TRUE: break num = num + D_BIT_ZERO i = d_next(i) return num def d_float_bin_parts(num): """浮点数整数部分和小数部分的二进制表示""" le = d_len(num) # d_len_half = int(d_len(num) / 2) d_len_half, _ = d_frac(le, D_TWO) # int_part = num[:d_len_half] int_part = d_slice(num, D_ZERO, d_len_half) # decimal_part = num[d_len_half:] # from to # 00000000 00001000 # from to # 00001000 00010000 decimal_part = d_slice(num, d_len_half, le) return int_part, decimal_part def d_mul_float(f1, f2): """ 浮点数乘法 以1.5*1.25为例: i1 = 1 d1=0.5 i2 = 1 d2=0.25 p1 = i1*i2 = 1 p2 = i1*d2 = 0.25 p3 = i2*d1 = 0.5 p4 = 0.5*0.25 = 0.125 """ # 预留浮点数越界标识 TODO 扩容处理, 科学计数法 of = D_FALSE i1, d1 = d_float_bin_parts(f1) i2, d2 = d_float_bin_parts(f2) # 记p1=i1*i2 p2=i1*d2 p3=i2*d1 p4=d1*d2 p1_i, _ = d_mul(i1, i2) # f1整数部分乘f2小数部分 p2_i, p2_d = d_mul_int_dec(i1, d2) # f2整数部分乘f1小数部分 p3_i, p3_d = d_mul_int_dec(i2, d1) # f1小数部分乘f2小数部分 p4_d = d_mul_dec(d1, d2) i, _ = d_plus(p1_i, p2_i) i, _ = d_plus(i, p3_i) d, _ = d_plus(p2_d, p3_d) d, _ = d_plus(d, p4_d) res = i + d return res def d_expand_left(num): """扩容,长度变为原来的二倍,前面补零""" return d_zero(d_len(num)) + num def d_expand_right(num): """扩容,长度变为原来的二倍,后面补零""" return num + d_zero(d_len(num)) def d_cut_right(num): """截掉右侧长度的一半""" # d_len_half = int(d_len(num) / 2) d_len_half, _ = d_frac(d_len(num), D_TWO) # res = num[:d_len] res = d_slice(num, D_ZERO, d_len_half) return res def d_cut_left(num): """截掉左侧长度的一半""" # d_len = int(d_len(num) / 2) d_len_half, _ = d_frac(d_len(num), D_TWO) # res = num[d_len:] res = d_slice(num, d_len_half, d_len(num)) return res def d_mul_int_dec(m, n): """ 整数部分与小数部分相乘 主要考虑越界问题,先扩充位数,再相乘,然后截出整数和小数部分 """ m_ex = d_expand_left(m) n_ex = d_expand_left(n) num, _ = d_mul(m_ex, n_ex) i, d = d_float_bin_parts(num) return i, d def d_mul_dec(m, n): """ 小数部分相乘 0.m * 0.n = m/10 * n/10 = mn/100 """ m_cut = d_cut_right_zeros(m) n_cut = d_cut_right_zeros(n) ma, na = d_alin(m_cut, n_cut) res, _ = d_mul(ma, na) res = d_align_right(res) res = d_right(res) res = d_right(res) return res def d_alin(m, n): """ 两个数对齐 以长度较大的数为基准 """ if d_len(m) < d_len(n): i = D_ZERO # i in range(d_len(n) - d_len(m)) max = d_minus(d_len(n), d_len(m)) while D_TRUE == D_TRUE: if d_is(i, max) == D_TRUE: break m = D_BIT_ZERO + m i = d_next(i) if d_len(m) > d_len(n): i = D_ZERO # i in range(d_len(m) - d_len(n)) max = d_minus(d_len(m), d_len(n)) while D_TRUE == D_TRUE: if d_is(i, max) == D_TRUE: break n = D_BIT_ZERO + n i = d_next(i) return m, n def d_align_right(num): """右侧补零""" i = D_ZERO # i >=0 且 i < D_LENGTH 即i in range(0, D_LENGTH) while D_TRUE == D_TRUE: if d_is(i, D_LENGTH) == D_TRUE: break num = num + D_BIT_ZERO i = d_next(i) return num def d_len(o_set): """ 返回num长度 num可以是任意的有序集合,只需提供一个遍历方法 """ res = D_ZERO for n in o_set: res = d_next(res) return res def d_index_of(o_set, e): """ 返回o_set中元素e的索引 如果不存在则返回负一(注意不是-1) """ i = D_ZERO for n in o_set: if d_is(n, e) == D_TRUE: return i i = d_next(i) return d_one_neg() def d_equal(o1, o2): """ 比较两个集合是否相等 """ i1 = D_ZERO for e1 in o1: i2 = D_ZERO for e2 in o2: if d_is(i2, i1) == D_TRUE: break i2 = d_next(i2) # 元素比较是集合的应有之义 if not e1 == e2: return D_FALSE i1 = d_next(i1) return D_TRUE def d_index(o_set, index): """ 返回index位置的o_set集合元素 index从0开始 """ # TODO 关于ZERO长度不足的问题 # 关于负索引,-i即length-i-1 # TODO 关于负数的规定 i = D_ZERO # while d_bit_and(d_less_or_is(D_ZERO, i), d_less(i, d_len(o_set))) == D_TRUE: for n in o_set: if d_is(i, index) == D_TRUE: return n i = d_next(i) return '-1' def d_slice(o_set, index_from, index_to): res = D_BLANK i = D_ZERO for n in o_set: if d_bit_and(d_less_or_is(index_from, i), d_less(i, index_to)) == D_TRUE: res = res + n i = d_next(i) return res def d_bin_dec_exp(num): # 25 = 2*10^2 + 5*10^1 """ 十进制转二进制小数部分表示 首先取各十进制位t1 t2 ... tn 对于一个形如.t1 t2 ... tn的十进制数,其值为 t1*10^-1 + t2*10^-2 +... + tn*10^-n 乘以10^n转为t1*10^(n-1) + t2*10(n-2) + ... + tn 求出此数的二进制表示,然后除以10^n的二进制表示 如0.25,放大100倍得25 25 = (d*2^-1 + d*2^-2 + ... + d*2^-n) * 100 两侧再同时乘以 2^n 25 * 2^n = (d*2^(n-1) + d*2^(n-2) + ... + d) * 100 然后再用(25 * 2^n)/100,得整数即为要求的小数部分 这里的越界问题用增加位数处理 """ num_bin = d_bin_exp(num) scale_idx = d_bin_exp(str(len(num))) scale = d_power(D_TEN, scale_idx) num_bin_ex = d_expand_left(num_bin) d_ones_expand = d_next(d_expand_left(d_ones())) mul_ex, _ = d_mul(num_bin_ex, d_ones_expand) scale_ex = d_expand_left(scale) q, _ = d_frac(mul_ex, scale_ex) res = d_cut_left(q) return res def test(): global D_LENGTH # test_d_ten_dec_exp() # pass # test_d_mul() # pass # test_d_bin_dec_exp() # pass # test_d_mul_float() # TODO # test_d_float_bin_exp() # pass # test_d_bin_exp() # pass # test_d_float_ten_exp() # pass # test_d_ten_exp() # pass # test_d_next() # pass # test_d_frac() # pass # test_d_minus() # pass # test_d_ones_neg() # pass # test_d_prev() # pass # test_d_plus() # pass # test_d_len() # pass # test_d_index() # pass # test_d_xor() # pass # test_d_bit_xor() # pass # test_d_less() # pass # test_d_zero() # pass # test_py() # test_d_power() # pass def test_d_power(): m = '00001010' n = '00000010' r = d_power(m, n) print(d_ten_exp(r)) def test_d_zero(): n = D_TEN r = d_zero(n) print(r) def test_d_less(): m = '0000000000000001' n = '0000000000001010' r = d_less(m, n) print(r) def test_d_plus(): m = '00000010' n = '00000111' r, _ = d_plus(m, n) print(r) def test_py(): s = '123' sl = list(s) print(sl.pop()) def test_d_bit_xor(): m = '1' n = '1' r = d_bit_xor(m, n) # 0 print(r) m = '0' n = '0' r = d_bit_xor(m, n) # 0 print(r) m = '1' n = '0' r = d_bit_xor(m, n) # 1 print(r) m = '0' n = '1' r = d_bit_xor(m, n) # 1 print(r) def test_d_is(): num1 = "00000000" num2 = "00000010" r = d_is(num1, num2) print(r) def test_d_xor(): # m = n 当且仅当 m xor n = 零 # m = '00000000' # n = '00000000' # r = d_xor(m, n) # # 00000000 # print(r) # m = '11111111' # n = '11111111' # r = d_xor(m, n) # print(r) m = '11111011' n = '11111011' r = d_xor(m, n) print(r) def test_d_index(): num = '123' index = D_TWO r = d_index(num, index) print(r) def test_d_len(): num = '123' l = d_len(num) print(l) def test_d_ten_dec_exp(): num = '00100000' r = d_ten_dec_exp(num) print(r) def test_d_mul(): # m = '4' # n = '4' # m_bin = d_bin_exp(m) # n_bin = d_bin_exp(n) # r, o = d_mul(m_bin, n_bin) # r_ten = d_ten_exp(r) # print(r_ten) # '0000000011111110' # '0000000000001010' m_bin = '0000000011111110' n_bin = '0000000000001010' r, o = d_mul(m_bin, n_bin) # 10011110110 # 0000000100010000' # r_ten = d_ten_exp(r) print(r) def test_d_bin_dec_exp(): m = '25' r = d_bin_dec_exp(m) print(r) def test_d_mul_float(): m = '10.5' n = '10.25' # m = d_float_bin_exp(m) m = '0000101010000000' # n = d_float_bin_exp(n) n = '0000101001000000' r = d_mul_float(m, n) # rt = d_float_ten_exp(r) print(r) def test_d_float_bin_exp(): m = '1.01' r = d_float_bin_exp(m) print(r) def test_d_bin_exp(): m = '127' r = d_bin_exp(m) print(r) def test_d_float_ten_exp(): # '7654321001234567' m = '0000000100011001' r = d_float_ten_exp(m) print(r) m = '0000000110000000' r = d_float_ten_exp(m) print(r) def test_d_ten_exp(): m = '01111111' r = d_ten_exp(m) print(r) def test_d_next(): m = '11111111' r = d_next(m) print('test_d_next: ', r) def test_d_prev(): # m = '01111111' # r = d_prev(m) # print(r) # m = '11111111' # r = d_prev(m) # print(r) m = '00001010' r = d_prev(m) print(r) def test_d_ones_neg(): r = d_one_neg(D_LENGTH) print(r) def test_d_minus(): m = '00000000' n = '00000010' r = d_minus(m, n) print("test_d_minus:", r) def test_d_frac(): m = '01111111' # 127 n = '00001010' # 10 d, r = d_frac(m, n) print("test_d_frac:", d_ten_exp(d), d_ten_exp(r)) def pi_ele(k): kex = d_expand_right(k) ONE = d_expand_right(D_ONE) TWO = d_expand_right(D_TWO) dm, _ = d_mul(TWO, kex) dp, _ = d_plus(dm, ONE) res, _ = d_frac(ONE, dp) return res def pi(k): # pi / 4 = 1 - 1/3 + 1/5 - 1/7 + ..... + (-1)^k (1 / (2k+1) ) + .... pi = d_expand_left(D_ZERO) i = D_ZERO while D_TRUE == D_TRUE: if d_is(i, k) == D_TRUE: break _, r = d_frac(i, D_TWO) if d_is(r, D_ZERO): pi = d_plus(pi, pi_ele(i)) else: pi = d_minus(pi, pi_ele(i)) i = d_next(i) print(pi) # 主方法 if __name__ == '__main__': # TODO 科学计数法解决乘法效率问题 test() pi(D_FOUR)
-
摘要:本篇文章结合灰度三维图像讲解图像顶帽运算和图像黑猫运算,通过Python调用OpenCV函数实现。本文分享自华为云社区《[Python图像处理] 十三.基于灰度三维图的图像顶帽运算和黑帽运算》,作者: eastmount。本篇文章继续深入,结合灰度三维图像讲解图像顶帽运算和图像黑猫运算,通过Python调用OpenCV函数实现。一.图像顶帽运算图像顶帽运算(top-hat transformation)又称为图像礼帽运算,它是用原始图像减去图像开运算后的结果,常用于解决由于光照不均匀图像分割出错的问题。其公式定义如下:图像顶帽运算是用一个结构元通过开运算从一幅图像中删除物体,校正不均匀光照的影响,其效果图如下图所示。在Python中,图像顶帽运算主要调用morphologyEx()实现,其中参数cv2.MORPH_TOPHAT表示顶帽处理,函数原型如下:dst = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel)src表示原始图像cv2.MORPH_TOPHAT表示图像顶帽运算kernel表示卷积核,可以用numpy.ones()函数构建假设存在一张光照不均匀的米粒图像,如图所示,我们需要调用图像顶帽运算解决光照不均匀的问题。其Python代码如下所示:#encoding:utf-8 import cv2 import numpy as np #读取图片 src = cv2.imread('test06.png', cv2.IMREAD_UNCHANGED) #设置卷积核 kernel = np.ones((10,10), np.uint8) #图像顶帽运算 result = cv2.morphologyEx(src, cv2.MORPH_TOPHAT, kernel) #显示图像 cv2.imshow("src", src) cv2.imshow("result", result) #等待显示 cv2.waitKey(0) cv2.destroyAllWindows()其运行结果如下,它有效地将米粒与背景分离开来。二.图像黑帽运算图像底帽运算(bottom-hat transformation)又称为图像黑帽运算,它是用图像闭运算操作减去原始图像后的结果,从而获取图像内部的小孔或前景色中黑点,也常用于解决由于光照不均匀图像分割出错的问题。其公式定义如下:图像底帽运算是用一个结构元通过闭运算从一幅图像中删除物体,常用于校正不均匀光照的影响。其效果图如下图所示。在Python中,图像底帽运算主要调用morphologyEx()实现,其中参数cv2.MORPH_BLACKHAT表示底帽或黑帽处理,函数原型如下:dst = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel)src表示原始图像cv2.MORPH_BLACKHAT表示图像底帽或黑帽运算kernel表示卷积核,可以用numpy.ones()函数构建Python实现图像底帽运算的代码如下所示:#encoding:utf-8 import cv2 import numpy as np #读取图片 src = cv2.imread('test06.png', cv2.IMREAD_UNCHANGED) #设置卷积核 kernel = np.ones((10, 10), np.uint8) #图像黑帽运算 result = cv2.morphologyEx(src, cv2.MORPH_BLACKHAT, kernel) #显示图像 cv2.imshow("src", src) cv2.imshow("result", result) #等待显示 cv2.waitKey(0) cv2.destroyAllWindows()其运行结果如图所示:三.基于灰度三维图的顶帽黑帽运算为什么图像顶帽运算会消除光照不均匀的效果呢?通常可以利用灰度三维图来进行解释该算法。灰度三维图主要调用Axes3D包实现,对原图绘制灰度三维图的代码如下:# -*- coding: utf-8 -*- import numpy as np import cv2 as cv import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib.ticker import LinearLocator, FormatStrFormatter #读取图像 img = cv.imread("test06.png") img = cv.cvtColor(img,cv.COLOR_BGR2GRAY) imgd = np.array(img) #image类转numpy #准备数据 sp = img.shape h = int(sp[0]) #图像高度(rows) w = int(sp[1]) #图像宽度(colums) of image #绘图初始处理 fig = plt.figure(figsize=(16,12)) ax = fig.gca(projection="3d") x = np.arange(0, w, 1) y = np.arange(0, h, 1) x, y = np.meshgrid(x,y) z = imgd surf = ax.plot_surface(x, y, z, cmap=cm.coolwarm) #自定义z轴 ax.set_zlim(-10, 255) ax.zaxis.set_major_locator(LinearLocator(10)) #设置z轴网格线的疏密 #将z的value字符串转为float并保留2位小数 ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) # 设置坐标轴的label和标题 ax.set_xlabel('x', size=15) ax.set_ylabel('y', size=15) ax.set_zlabel('z', size=15) ax.set_title("surface plot", weight='bold', size=20) #添加右侧的色卡条 fig.colorbar(surf, shrink=0.6, aspect=8) plt.show()运行结果如下图所示:从图像中的像素走势显示了该图受各部分光照不均匀的影响,从而造成背景灰度不均现象,其中凹陷对应图像中灰度值比较小的区域。而通过图像白帽运算后的图像灰度三维图的代码如下:# -*- coding: utf-8 -*- import numpy as np import cv2 as cv import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm from matplotlib.ticker import LinearLocator, FormatStrFormatter #读取图像 img = cv.imread("test06.png") img = cv.cvtColor(img,cv.COLOR_BGR2GRAY) #图像黑帽运算 kernel = np.ones((10,10), np.uint8) result = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel) #image类转numpy imgd = np.array(result) #准备数据 sp = result.shape h = int(sp[0]) #图像高度(rows) w = int(sp[1]) #图像宽度(colums) of image #绘图初始处理 fig = plt.figure(figsize=(8,6)) ax = fig.gca(projection="3d") x = np.arange(0, w, 1) y = np.arange(0, h, 1) x, y = np.meshgrid(x,y) z = imgd surf = ax.plot_surface(x, y, z, cmap=cm.coolwarm) #自定义z轴 ax.set_zlim(-10, 255) ax.zaxis.set_major_locator(LinearLocator(10)) #设置z轴网格线的疏密 #将z的value字符串转为float并保留2位小数 ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) # 设置坐标轴的label和标题 ax.set_xlabel('x', size=15) ax.set_ylabel('y', size=15) ax.set_zlabel('z', size=15) ax.set_title("surface plot", weight='bold', size=20) #添加右侧的色卡条 fig.colorbar(surf, shrink=0.6, aspect=8) plt.show()效果图如下所示,对应的灰度更集中于10至100区间,由此证明了不均匀的背景被大致消除了,有利于后续的阈值分割或图像分割。
-
【功能模块】WeAutomate studio 2.18,“公共”/“编程&调试”/“执行python语句”【操作步骤&问题现象】1、在Weautomate(2.18)设计器中添加控件“执行python语句”。2、在“执行python语句”中,创建DataFrame类型的变量df1.3、请问如何传出该变量,以便在其他控件,比如使用创建好的df1写入到Excel文件中.【已尝试方案】1、通过全局变量:创建全局变量x1,在"执行python语句"中,写入global@{x1}=df1,因为全局变量不支持DataFrame类型,x1类型为object.(失败)2、通过全局参数:创建全局参数x2,在"执行python语句"中,写入global@{x2}=df1.(失败)3、参考weautomate自带pdf文档,没有找到对应说明。(失败)该如何操作,请赐教,谢谢!
-
算术运算符运算符描述+两个数相加两个数相加,或是字符串连接-两个数相减*两个数相乘,或是返回一个重复若干次的字符串/两个数相除,结果为浮点数%取模,返回两个数相除的余数//两个数相除,返回商的整数部分**幂运算,返回乘方结果1234567print(1 + 2) # 3print(2 - 1) # 1print(2 * 3) # 6print(3 / 2) # 1.5print(6 % 5) # 1print(8 // 2) # 4print(3 ** 2) # 9如果想同时得到商和余数,可以用divmod这个方法该方法的返回值是tuple (x//y, x%y).1print(divmod(10,3)) # (3,1)因为浮点数精度的问题,Python还存在一些计算方面的小问题1print(0.1+0.1+0.1-0.3) # 5.551115123125783e-17要解决这个问题,可以导入decimal模块123from decimal import Decimal# 计算结果:0.0print(Decimal('0.1')+Decimal('0.1')+Decimal('0.1')-Decimal('0.3'))关系运算符运算符描述==比较对象是否相等!=比较对象是否不相等>大于,例如a>b,将比较a与b的大小,a>b返回True,否则返回False<小于,例如a<b,将比较a与b的大小,a<b返回True,否则返回False>=大于等于,例如a>=b,将比较a与b的大小,a>=b返回True,否则返回False<=小于等于,例如a<=b,将比较a与b的大小,a<=b返回True,否则返回False12345678a = 10b = 20print(a == b) # Falseprint(a != b) # Trueprint(a > b) # False print(a < b) # Trueprint(a >= b) # Falseprint(a <= b) # True1.返回值为bool值12print(True == 1) # Trueprint(False == 0) # True==比较数值(int与str不能直接比较)12print(2.0 == 2) # Trueprint('2' == 2) # False字符串与字符串之间是比较ASCII值12# Trueprint('abc' < 'xyz') # 97 98 99 < 120 121 122连续比较,python的解释机制123print(3 > 2 > 1) # Trueprint(3 > 2 > 2) # Falseprint((3 > 2) > 1) # False赋值运算符运算符描述=常规赋值运算符,将运算结果赋值给变量+=加法赋值运算符,例如 a+=b 等效于 a=a+b-=减法赋值运算符,例如 a-=b 等效于 a=a-b*=乘法法赋值运算符,例如 a*=b 等效于 a=a*b/=除法赋值运算符,例如 a/=b 等效于 a=a/b//=取整除赋值运算符,例如 a//=b 等效于 a=a//b%=取模赋值运算符,例如 a%=b 等效于 a=a%b**=幂运算赋值运算符,例如 a**=b 等效于 a=a*b123a = 1 # 将等号右边 赋值 等号左边a = a + 1 # 先等号右边计算 再赋值给 等号左边a += 1 # a = a + 1逻辑运算符运算符描述and与运算,如果a 为False,a and b返回False 否则返回y的计算值or或运算,如果a非0,返回a的值,否则返回b的值not非运算,如果a为True,返回False,如果a为False,返回True12345a = 10b = 20print(a and b) # 20print(a or b) # 10print(not a) # Falsea and b 两者都为True时结果才为Truea or b 两者有一个为True则结果就为True短路(懒惰)原则False and True 当and运算符时,遇到第一个为False则不会往后了True or False 当or运算符时,遇到第一个为True则不会往后了成员运算符运算符描述in如果在指定的序列中找到值返回True,否则返回Falsenot in如果在指定的序列中找到值返回True,否则返回False123456list1 = [1, 2, 3, 4, 5]a = 1if a in list1: print("a是list1的元素之一")else: print("a不是list1的元素")身份运算符运算符描述is判断两个标识符是不是引用自一个对象is not判断两个标识符是不是引用自不同对象 is 和比较运算符 == 的区别 is 用于判断两个变量是否引自同一个对象(可使用id()查看),而 ==用于判断变量的值是否相等!1234567a = [1, 2, 3]b = [1, 2, 3]# 可通过id()查看内存地址print(id(a)) # 2042443551304print(id(b)) # 2042443551816print(a is b) # Falseprint(a == b) # True1234567a = 2b = 2.0# 可通过id()查看内存地址print(id(a)) # 140722443350320print(id(b)) # 2336678499216print(a is b) # Falseprint(a == b) # True三目运算符三目运算符的表示方法:True_statements if expression else False_statements1234a = 1b = 2# a+b不大于3执行后面的else语句 b-a = 1print(a+b if a+b>3 else b-a)运算符优先级运算符描述**指数(最高优先级)*/%//乘,除,取模和取整除±加法减法<= >=比较运算符== !=等于运算符= %= 、= 、、=-= += *= **=赋值运算符is is not身份运算符in not in成员运算符not or and逻辑运算符
-
格式化字符串当我们需要创建文本或文档时,我们往往希望能够将值正确格式化为结构化字符串。 Python提供了多种选项用于正确对字符串进行格式化并呈现。本节,我们将介绍创建格式化文本时的一些常用选项。Format 方法在 Python 通常使用format方法格式化字符串,通常使用以下方式利用 format方法显示变量:1result = template.format(*parameters)其中,模板 (template) 是一个需要使用变量进行解释的字符串。在最简单的情况下,模板将大括号之间的值替换为 parameters 变量值,为了更好的理解,我们查看以下简单示例:12345678>>> 'My name is {}'.format('Brainiac')'My name is Brainiac'>>> 'The price of ({}) is {}'.format('apple',6.28)'The price of (apple) is 6.28'>>> 'And all of that happens in the {1} {0}.'.format('second','first')'And all of that happens in the first second.'>>> 'The {fruit} should be eaten within {number} days.'.format(number=3, fruit='banana')'The banana should be eaten within 3 days.'在绝大多数情况下,我们只需要这种格式就能应对。但是对于复杂情况,例如自动对齐字符串和创建排版美观的文本时,format方法也提供了更多的选择。接下来,我们编写脚本 format_string.py来打印需要对齐的表格:123456789101112131415# 输入数据data = [ (1615.8, 1604.1), (183.3, 152.8), (1254.9, 1308.1), (155.4, 161.0),]# 表头print(' Q1 FY21 | Q1 FY22 | Change')# 以正确的格式对齐和显示数据TEMPLATE = '{fy21:>+8,} | {fy22:>+8} | {change:>8.3}'# 打印数据for fy21, fy22 in data: row = TEMPLATE.format(fy21=fy21, fy22=fy22, change=fy22 - fy21) print(row)运行以上脚本以显示对齐后的表格:123456$ python format_string.py Q1 FY21 | Q1 FY22 | Change+1,615.8 | +1604.1 | -11.7 +183.3 | +152.8 | -30.5+1,254.9 | +1308.1 | 53.2 +155.4 | +161.0 | 5.6在以上脚本中,使用 TEMPLATE 常量定义了一个具有三列的表格,每一列由一个名为 fy21、fy22和 change的参数定义,便于将模板应用于格式调用。用于定义格式所有内容都在大括号内。在参数名称之后,使用一个冒号分隔格式定义,在所有列中,我们都将宽度设置为8个字符,以确保所有列具有相同的宽度,并使用 > 符号将值向右对齐:Fy21 带有 “,” 符号的作为千位分隔符 —— {fy21:>+8,}Fy21 和fy22为正值,因此使用 “+” 号用于自动添加数值符号 —— {fy21:>+8,}、{fy22:>+8}Change 使用 “.” 符号来声明显示精度 —— {change:>8.3}其它格式化字符串方法有时,我们还可能还看到过使用%运算符的来设置Python格式,但它不如format方法灵活,因此,已不推荐使用。此外,自Python 3.6以来Python中新增了一个很棒的特性是使用f-strings进行格式化,它使用定义的变量执行格式化操作:1234>>> p1 = 'first'>>> p2 = 'second'>>> f'And all of that happens in the {p1} {p2}.''And all of that happens in the first second.'使用这种特性可以简化很多代码,并允许我们创建非常具有可读性的代码。
-
behave是python语言的行为驱动开发,全称:Behavior-driven development,简称BDD。BDD框架BDD即行为驱动开发(Behavior Driven Development),其特点为:通过自然语言来定义系统行为从功能使用者的角度,编写需求场景鼓励软件项目中的开发者、非技术人员以及商业参与者之间的协作。协作的核心是通过活文档或者说场景文档来协作,该文档既是测试用例文档,也是需求定义文档常见的BDD框架有Behave,Cucumber等它是一种敏捷软件开发技术,它鼓励软件项目中的开发人员、QA和非技术或业务参与者之间进行协作。python behave的官方网址:https://behave.readthedocs.io/en/latest/gherkin.html#gherkin-feature-testing-language最初由Dan North命名,并于2009年对BDD给出了如下定义:“BDD是第二代、由外而内、基于拉动、多利益相关者、多规模、高度自动化、敏捷的方法。它描述了一个与定义明确的输出交互的循环,从而交付了重要的工作、测试软件。”BDD并不会描述或定义软件怎么做,而是能做了什么。最终通过python代码进行验证。首先用pycharm创建项目Python-Behave,python环境选择Virtualenv,接着安装behave包。在项目Python-Behave下创建一个名为“features”的目录(这个目录名称是随意的),可以在这个目录下定义所有behave的文件结构。在features目录下创建一个“.feature”文件,这是一种叫作“Gherkin”的语言。它对非技术人员比较友好,可以使用自然语言编写。“.feature”文件有两个用途:文档和自动化测试。一句话,在“.feature”里编写测试场景。很多文章提到Gherkin语言必须用pycharm专业版才能编写,但是我亲测用pycharm社区版也是可以编写的。“.feature”文件的结构:主体由多个场景Scenario组成,可以选用Background和tag进行约束。feature文件的一个基本的结构为:123456Feature: feature name Scenario: some scenario Given some condition When some operation Then some result is expectedFeature是功能名称Scenario是场景描述Given是此场景下的前提条件When是此场景下的操作步骤Then是此场景下的预期结果如果有多个测试场景呢,就再加一个Scenario。如果Scenario下的Given/When/Then有多个呢?可以用And或But表示。所以1234567Scenario: Multiple Givens Given one thing Given another thing Given yet another thing When I open my eyes Then I see something Then I don't see something else也可以这样写作1234567Scenario: Multiple Givens Given one thing And another thing And yet another thing When I open my eyes Then I see something But I don't see something else这种方式阅读会更流畅当然,上面只是一个简单的feature结构,更复杂一点的,比如说这样:1234567891011121314151617181920@tags @tagFeature: feature name description further description Background: some requirement of this test Given some setup condition And some other setup action Scenario: some scenario Given some condition When some action is taken Then some result is expected. Scenario: some other scenario Given some other condition When some action is taken Then some other result is expected. Scenario: ...Background由一系列类似于Scenario的步骤组成,它的目的是为Scenario添加上下文,在Scenario执行之前执行Background,用于设置Scenario的前提条件。Scenario由一系列步骤组成,它描述了Feature的一种场景。如果一种场景有多种情况呢?比如登录这个Scenario,不同的登录名和密码,登录的结果不同。这种情况可以不需要写多个Scenario描述,可以使用Scenario Outline和Examples来完成。12345678910111213Scenario Outline: Blenders Given I put <thing> in a blender, when I switch the blender on then it should transform into <other thing> Examples: Amphibians | thing | other thing | | Red Tree Frog | mush | Examples: Consumer Electronics | thing | other thing | | iPhone | toxic waste | | Galaxy Nexus | toxic waste |在上面这个例子中,用Scenario Outline描述“Blenders”场景,用多个Examples表示场景的多种类型,每个Examples下可以包含多种情况。不同情况的列举在Scenario Outline用符号“<key>”表示,在Examples中用key列举Scenario由一系列步骤组成,步骤由关键字“Given”、“When”、“Then”、“And”、“But”为开头。Python Behave实际运行的也是这些步骤。具体实现是通过此项目下的steps目录里的“.py”文件实现所有的Scenario的步骤。这里要注意,steps目录名是确定的不能改变的,但是里面的py文件名是随意的。python behave项目的执行方式也并不是通过运行steps目录里的py文件,而是通过命名behave调用“.feature”文件,映射到py文件里的步骤下的函数,执行这些函数。步骤描述要尽量简洁,但有时会附带一些文本text或表格table。如果python代码需要使用这些text或table,则可以通过访问属性“context.text”或”context.table“来使用。Text:12345678910111213Scenario: some scenario Given a sample text loaded into the frobulator """ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. """ When we activate the frobulator Then we will find it similar to EnglishTable:12345678910Scenario: some scenario Given a set of specific users | name | department | | Barry | Beer Cans | | Pudey | Silly Walks | | Two-Lumps | Silly Walks | When we count the number of people in each departmentThen we will find two people in "Silly Walks" But we will find one person in "Beer Cans"Python中访问:1234@given('a set of specific users')def step_impl(context): for row in context.table: model.add_user(name=row['name'], department=row['department']) 这个表格在python中是这样的数据类型:1[{"name":"Barry", "department":"Beer Cans"},{"name":"Pudey", "department":"Silly Walks"},{"name":"Two-Lumps", "department":"Silly Walks"}] 认真体会下!!!Tags:tags用于标记Feature、Scenario或Scenario Outlook,可以选择性的只执行被标记的。例如:12345678910111213141516Feature: Fight or flight In order to increase the ninja survival rate, As a ninja commander I want my ninjas to decide whether to take on an opponent based on their skill levels @slowScenario: Weaker opponent Given the ninja has a third level black-belt When attacked by a samurai Then the ninja should engage the opponent Scenario: Stronger opponent Given the ninja has a third level black-belt When attacked by Chuck Norris Then the ninja should run for his life 如果只想执行Scenario: Weaker opponent,可以对它进行标记为@slow,然后运行“behave --tags=slow”。如果想执行除标记@slow外的其他场景,可以运行“behave --tags=“not slow””。组合使用tags标签:–tags=“wip or slow”,选择所有标记为wip或slow的case–tags=“wip and slow”,选择所有标记为wip和slow的case以上讲的是如何用“.feature”文件编写所有测试场景。虽然是通过命令behave xxx.feature触发,但实际的执行操作是在steps目录下的“.py”实现。所以,要如何把“.feature”里的所有步骤映射到“.py”中,还必须按照顺序不能出错,这就成为了一个关键。假设给定一个Senario:123Scenario: Search for an account Given I search for a valid account Then I will see the account details记住,只对所有的步骤按顺序实现,并不会对Scenario进行映射。在python中实现如下:1234567891011121314@given('I search for a valid account')def step_impl(context): context.browser.get('http://localhost:8000/index') form = get_element(context.browser, tag='form') get_element(form, name="msisdn").send_keys('61415551234') form.submit() @then('I will see the account details')def step_impl(context): elements = find_elements(context.browser, id='no-account') eq_(elements, [], 'account not found') h = get_element(context.browser, id='account-head') ok_(h.text.startswith("Account 61415551234"), 'Heading %r has wrong text' % h.text) 按照“.feature”中的步骤的顺序,将步骤的前面的关键字,在python中用装饰器匹配(@give、@when、@then),()里面是步骤描述。如果是And或But,在python中用它们被重命名以前的关键字。也就是说,在python中,不可能有@and或@but。然后在装饰器下方定义函数,函数名随意。函数体就是步骤的具体实现。如果你想在某个步骤里执行另一个步骤,只需要用context对象调用execute_steps()函数,里面传入被调用步骤。123456@when('I do the same thing as before')def step_impl(context): context.execute_steps(''' when I press the big red button and I duck ''') 如果你想把“.feature”的步骤上的信息传递到“.py”中,可以在py文件中用中括号加关键字表示“{key}”。比如访问百度首页:1234Scenario: 访问百度网页 Given: 打开网页 When: 输入网址www.baidu.com Then:显示百度首页 关于其中输入百度网址,在python中就可以这样实现:123@when(输入网址{baidu})def step_imp1(context, baidu): context.driver.get(baidu) Context:聪明的你一定注意到了,在py文件中每个步骤下的函数内第一个参数是context,它是Feature或Scenario的实例化,可以用来传递信息。如何传递呢,这里我们暂且按下不表,先思考一个问题。如果我有一个场景,打开百度网页,输入关键字搜索,然后查看搜索结果。在feature文件中如何描述,这个我们应该已经学会了。在py文件中如何实现,我们也不陌生了。1234Scenario: 打开百度网页并输入关键字 Given: 打开百度网页http://www.baidu.com When: 输入关键字大美女 Then:验证返回的搜索结果标题是大美女_百度搜索 12345678@given(打开百度网页{baidu})def step_imp1(context, baidu): context.driver.get(baidu) @when(输入关键字{keyword})def step_imp1(context, keyword): context.driver.find_element_by_xpath('//*[@id="kw"]').send_keys(keyword) ...后面我就不写了。现在只看given的步骤,你应该就能发现问题,context.driver哪来的,从已知的上下文中并没有相关信息。如果你再仔细的思考一下,就会发现这里少了一步,我们在打开百度网站前,是不是应该先打开浏览器,然后才是输入百度网址。如果你有多个关于网页搜索的场景,你是不是应该每次执行Scenario前都要打开浏览器,执行完毕关闭浏览器。这个操作类似于python unittester中的setup()和teardown()的用法。那python behave框架中,对于操作前和操作后的前置条件和后置条件,是放在了“environment.py”文件中定义。它有以下几种:before_step(context, step)和after_step(context, step),每一步之前和之后运行before_scenario(context, scenario)和after_scenario(context, scenario),每个场景运行之前和之后运行before_feature(context, feature)和after_feature(context, feature),每个feature文件执行之前和之后运行before_tag(context, tag)和after_tag(context, tag),每个标签调用之前和之后运行before_all(context)和after_all(context),整个behave之前和之后运行那上面的例子中,在environment.py中实现打开和关闭浏览器的操作,要这样实现:12345678# environment.pyfrom selenium import webdriver def before_scenario(context, scenario): context.driver = webdriver.Chrome(r"/usr/local/bin/chromedriver") def after_scenario(context, scenario): context.driver.close() 前置条件和后置条件在environment.py中用上面列举的函数直接定义,函数名和形参必须符合规范。可以看出,如果environment.py中的信息、变量或对象需要在steps中的py文件中被使用,可以用context来存储。这里就把打开的浏览器对象赋值给了context下定义的driver属性,然后在py文件中直接可以使用context.driver,就相当于使用这个浏览器了。注意注意,environment.py文件在python behave项目中的位置是哪里?是steps目录中吗?不是的,environment.py文件是和“.feature”文件、steps目录并列在同一目录下的。而且它的名称必须是environment.py。现在回头看下一个完整的python behave项目的最低结构要求:1234+--features/| +--steps/ # -- Steps directory| | +-- *.py # -- Step implementation or use step-library python files.| +-- *.feature # -- Feature files. 更复杂的目录为:123456789+-- features/| +-- steps/| | +-- website_steps.py| | +-- utils.py| || +-- environment.py # -- Environment file with behave hooks, etc.| +-- signup.feature| +-- login.feature| +-- account_details.feature 最后,如何执行behave的程序?不是执行的steps目录里的py文件,而是通过cmd打开命令行窗口,执行:“behave xxx.feature”。这样也很麻烦,有没有一种方式可以在py文件的主入口里执行"behave xxx.feature",这样可以封装成exe程序,双击运行exe,即可运行behave。在python behave项目目录下创建一个py文件(也就是和feature文件、steps目录文件在同一级下),命名为main.py,然后写一个主入口程序:12345678910111213141516171819# main.pyfrom behave.__main__ import main as behave_mainimport os if __name__ == "__main__": # 获取main.py的当前目录 featurefile_dir = os.path.dirname(os.path.realpath(__file__)) # 获取feature文件的绝对路径 featurefile_path = os.path.join(featurefile_dir, "Behave.feature") # 用behave.__main__下的main函数,传入feature文件路径,实现behave xxx.feature相同的效果 # 但是这里有个主要点,传入feature文件的路径不能是以这样“\”或这样“\\”的表示方式,必须要改成这种“/” featurefile_path = featurefile_path.replace("\\", "/") behave_main(featurefile_path) # 或者你不转成“\”,那么你就不能直接传入feature文件路径了,必须把文件路径放入一个list中,然后把list传入 # 如果你传入list,相当于传入多个参数,这时候除了文件路径,还可以传入tags参数,以执行标记的功能或场景 cmd_order = [] cmd_order.append(featurefile_path) # 把feature文件路径添加为list的第一个元素 cmd_order.append("-t @slow") # 把tag标签添加为list的第二个元素 behave_main(cmd_order) # 传入参数列表
-
目录web 框架中的 requestThreadLocalcontextvarsc.Tokencontextvars.Context小结Python 在 3.7 的时候引入了一个模块:contextvars,从名字上很容易看出它指的是上下文变量(Context Variables),所以在介绍 contextvars 之前我们需要先了解一下什么是上下文(Context)。Context 是一个包含了相关信息内容的对象,举个例子:"比如一部 13 集的动漫,你直接点进第八集,看到女主角在男主角面前流泪了"。相信此时你是不知道为什么女主角会流泪的,因为你没有看前面几集的内容,缺失了相关的上下文信息。所以 Context 并不是什么神奇的东西,它的作用就是携带一些指定的信息。web 框架中的 request我们以 fastapi 和 sanic 为例,看看当一个请求过来的时候,它们是如何解析的。1234567891011121314151617181920212223242526272829303132# fastapifrom fastapi import FastAPI, Requestimport uvicorn app = FastAPI() @app.get("/index")async def index(request: Request): name = request.query_params.get("name") return {"name": name} uvicorn.run("__main__:app", host="127.0.0.1", port=5555) # ------------------------------------------------------- # sanicfrom sanic import Sanicfrom sanic.request import Requestfrom sanic import response app = Sanic("sanic") @app.get("/index")async def index(request: Request): name = request.args.get("name") return response.json({"name": name}) app.run(host="127.0.0.1", port=6666)发请求测试一下,看看结果是否正确。可以看到请求都是成功的,并且对于 fastapi 和 sanic 而言,其 request 和 视图函数是绑定在一起的。也就是在请求到来的时候,会被封装成一个 Request 对象、然后传递到视图函数中。但对于 flask 而言则不是这样子的,我们看一下 flask 是如何接收请求参数的。123456789101112from flask import Flask, request app = Flask("flask") @app.route("/index")def index(): name = request.args.get("name") return {"name": name} app.run(host="127.0.0.1", port=7777)我们看到对于 flask 而言则是通过 import request 的方式,如果不需要的话就不用 import,当然我这里并不是在比较哪种方式好,主要是为了引出我们今天的主题。首先对于 flask 而言,如果我再定义一个视图函数的话,那么获取请求参数依旧是相同的方式,但是这样问题就来了,不同的视图函数内部使用同一个 request,难道不会发生冲突吗?显然根据我们使用 flask 的经验来说,答案是不会的,至于原因就是 ThreadLocal。ThreadLocalThreadLocal,从名字上看可以得出它肯定是和线程相关的。没错,它专门用来创建局部变量,并且创建的局部变量是和线程绑定的。1234567891011121314151617181920212223242526272829import threading # 创建一个 local 对象local = threading.local() def get(): name = threading.current_thread().name # 获取绑定在 local 上的 value value = local.value print(f"线程: {name}, value: {value}") def set_(): name = threading.current_thread().name # 为不同的线程设置不同的值 if name == "one": local.value = "ONE" elif name == "two": local.value = "TWO" # 执行 get 函数 get() t1 = threading.Thread(target=set_, name="one")t2 = threading.Thread(target=set_, name="two")t1.start()t2.start()"""线程 one, value: ONE线程 two, value: TWO"""可以看到两个线程之间是互不影响的,因为每个线程都有自己唯一的 id,在绑定值的时候会绑定在当前的线程中,获取也会从当前的线程中获取。可以把 ThreadLocal 想象成一个字典:1234{ "one": {"value": "ONE"}, "two": {"value": "TWO"}}更准确的说 key 应该是线程的 id,为了直观我们就用线程的 name 代替了,但总之在获取的时候只会获取绑定在该线程上的变量的值。而 flask 内部也是这么设计的,只不过它没有直接用 threading.local,而是自己实现了一个 Local 类,除了支持线程之外还支持 greenlet 的协程,那么它是怎么实现的呢?首先我们知道 flask 内部存在 "请求 context" 和 "应用 context",它们都是通过栈来维护的(两个不同的栈)。123456# flask/globals.py_request_ctx_stack = LocalStack()_app_ctx_stack = LocalStack()current_app = LocalProxy(_find_app)request = LocalProxy(partial(_lookup_req_object, "request"))session = LocalProxy(partial(_lookup_req_object, "session"))每个请求都会绑定在当前的 Context 中,等到请求结束之后再销毁,这个过程由框架完成,开发者只需要直接使用 request 即可。所以请求的具体细节流程可以点进源码中查看,这里我们重点关注一个对象:werkzeug.local.Local,也就是上面说的 Local 类,它是变量的设置和获取的关键。直接看部分源码:12345678910111213141516171819202122232425262728293031323334353637383940414243# werkzeug/local.py class Local(object): __slots__ = ("__storage__", "__ident_func__") def __init__(self): # 内部有两个成员:__storage__ 是一个字典,值就存在这里面 # __ident_func__ 只需要知道它是用来获取线程 id 的即可 object.__setattr__(self, "__storage__", {}) object.__setattr__(self, "__ident_func__", get_ident) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: # 根据线程 id 得到 value(一个字典) # 然后再根据 name 获取对应的值 # 所以只会获取绑定在当前线程上的值 return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: # 将线程 id 作为 key,然后将值设置在对应的字典中 # 所以只会将值设置在当前的线程中 storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): # 删除逻辑也很简单 try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)所以我们看到 flask 内部的逻辑其实很简单,通过 ThreadLocal 实现了线程之间的隔离。每个请求都会绑定在各自的 Context 中,获取值的时候也会从各自的 Context 中获取,因为它就是用来保存相关信息的(重要的是同时也实现了隔离)。相应此刻你已经理解了上下文,但是问题来了,不管是 threading.local 也好、还是类似于 flask 自己实现的 Local 也罢,它们都是针对线程的。如果是使用 async def 定义的协程该怎么办呢?如何实现每个协程的上下文隔离呢?所以终于引出了我们的主角:contextvars。contextvars该模块提供了一组接口,可用于在协程中管理、设置、访问局部 Context 的状态。12345678910111213141516171819202122232425import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试") async def get(): # 获取值 return c.get() + "~~~" async def set_(val): # 设置值 c.set(val) print(await get()) async def main(): coro1 = set_("协程1") coro2 = set_("协程2") await asyncio.gather(coro1, coro2) asyncio.run(main())"""协程1~~~协程2~~~"""ContextVar 提供了两个方法,分别是 get 和 set,用于获取值和设置值。我们看到效果和 ThreadingLocal 类似,数据在协程之间是隔离的,不会受到彼此的影响。但我们再仔细观察一下,我们是在 set_ 函数中设置的值,然后在 get 函数中获取值。可 await get() 相当于是开启了一个新的协程,那么意味着设置值和获取值不是在同一个协程当中。但即便如此,我们依旧可以获取到希望的结果。因为 Python 的协程是无栈协程,通过 await 可以实现级联调用。我们不妨再套一层:123456789101112131415161718192021222324252627282930import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试") async def get1(): return await get2() async def get2(): return c.get() + "~~~" async def set_(val): # 设置值 c.set(val) print(await get1()) print(await get2()) async def main(): coro1 = set_("协程1") coro2 = set_("协程2") await asyncio.gather(coro1, coro2) asyncio.run(main())"""协程1~~~协程1~~~协程2~~~协程2~~~"""我们看到不管是 await get1() 还是 await get2(),得到的都是 set_ 中设置的结果,说明它是可以嵌套的。并且在这个过程当中,可以重新设置值。12345678910111213141516171819202122232425262728293031323334353637383940import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试") async def get1(): c.set("重新设置") return await get2() async def get2(): return c.get() + "~~~" async def set_(val): # 设置值 c.set(val) print("------------") print(await get2()) print(await get1()) print(await get2()) print("------------") async def main(): coro1 = set_("协程1") coro2 = set_("协程2") await asyncio.gather(coro1, coro2) asyncio.run(main())"""------------协程1~~~重新设置~~~重新设置~~~------------------------协程2~~~重新设置~~~重新设置~~~------------"""先 await get2() 得到的就是 set_ 函数中设置的值,这是符合预期的。但是我们在 get1 中将值重新设置了,那么之后不管是 await get1() 还是直接 await get2(),得到的都是新设置的值。这也说明了,一个协程内部 await 另一个协程,另一个协程内部 await 另另一个协程,不管套娃(await)多少次,它们获取的值都是一样的。并且在任意一个协程内部都可以重新设置值,然后获取会得到最后一次设置的值。再举个栗子:12345678910111213141516171819202122232425262728import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试") async def get1(): return await get2() async def get2(): val = c.get() + "~~~" c.set("重新设置啦") return val async def set_(val): # 设置值 c.set(val) print(await get1()) print(c.get()) async def main(): coro = set_("古明地觉") await coro asyncio.run(main())"""古明地觉~~~重新设置啦"""await get1() 的时候会执行 await get2(),然后在里面拿到 c.set 设置的值,打印 "古明地觉~~~"。但是在 get2 里面,又将值重新设置了,所以第二个 print 打印的就是新设置的值。\如果在 get 之前没有先 set,那么会抛出一个 LookupError,所以 ContextVar 支持默认值:1234567891011121314151617181920import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试", default="哼哼") async def set_(val): print(c.get()) c.set(val) print(c.get()) async def main(): coro = set_("古明地觉") await coro asyncio.run(main())"""哼哼古明地觉"""除了在 ContextVar 中指定默认值之外,也可以在 get 中指定:1234567891011121314151617181920import asyncioimport contextvars c = contextvars.ContextVar("只是一个标识, 用于调试", default="哼哼") async def set_(val): print(c.get("古明地恋")) c.set(val) print(c.get()) async def main(): coro = set_("古明地觉") await coro asyncio.run(main())"""古明地恋古明地觉"""所以结论如下,如果在 c.set 之前使用 c.get:当 ContextVar 和 get 中都没有指定默认值,会抛出 LookupError;只要有一方设置了,那么会得到默认值;如果都设置了,那么以 get 为准;如果 c.get 之前执行了 c.set,那么无论 ContextVar 和 get 有没有指定默认值,获取到的都是 c.set 设置的值。所以总的来说还是比较好理解的,并且 ContextVar 除了可以作用在协程上面,它也可以用在线程上面。没错,它可以替代 threading.local,我们来试一下:1234567891011121314151617181920212223242526import threadingimport contextvars c = contextvars.ContextVar("context_var") def get(): name = threading.current_thread().name value = c.get() print(f"线程 {name}, value: {value}") def set_(): name = threading.current_thread().name if name == "one": c.set("ONE") elif name == "two": c.set("TWO") get() t1 = threading.Thread(target=set_, name="one")t2 = threading.Thread(target=set_, name="two")t1.start()t2.start()"""线程 one, value: ONE线程 two, value: TWO"""和 threading.local 的表现是一样的,但是更建议使用 ContextVars。不过前者可以绑定任意多个值,而后者只能绑定一个值(可以通过传递字典的方式解决这一点)。c.Token当我们调用 c.set 的时候,其实会返回一个 Token 对象:12345678import contextvars c = contextvars.ContextVar("context_var")token = c.set("val")print(token)"""<Token var=<ContextVar name='context_var' at 0x00..> at 0x00...>"""Token 对象有一个 var 属性,它是只读的,会返回指向此 token 的 ContextVar 对象。123456789101112import contextvars c = contextvars.ContextVar("context_var")token = c.set("val") print(token.var is c) # Trueprint(token.var.get()) # val print( token.var.set("val2").var.set("val3").var is c) # Trueprint(c.get()) # val3Token 对象还有一个 old_value 属性,它会返回上一次 set 设置的值,如果是第一次 set,那么会返回一个 <Token.MISSING>。12345678910111213import contextvars c = contextvars.ContextVar("context_var")token = c.set("val") # 该 token 是第一次 c.set 所返回的# 在此之前没有 set,所以 old_value 是 <Token.MISSING>print(token.old_value) # <Token.MISSING> token = c.set("val2")print(c.get()) # val2# 返回上一次 set 的值print(token.old_value) # val那么这个 Token 对象有什么作用呢?从目前来看貌似没太大用处啊,其实它最大的用处就是和 reset 搭配使用,可以对状态进行重置。123456789101112131415161718import contextvars#### c = contextvars.ContextVar("context_var")token = c.set("val")# 显然是可以获取的print(c.get()) # val # 将其重置为 token 之前的状态# 但这个 token 是第一次 set 返回的# 那么之前就相当于没有 set 了c.reset(token)try: c.get() # 此时就会报错except LookupError: print("报错啦") # 报错啦 # 但是我们可以指定默认值print(c.get("默认值")) # 默认值contextvars.Context它负责保存 ContextVars 对象和设置的值之间的映射,但是我们不会直接通过 contextvars.Context 来创建,而是通过 contentvars.copy_context 函数来创建。12345678910111213141516171819202122import contextvars c1 = contextvars.ContextVar("context_var1")c1.set("val1")c2 = contextvars.ContextVar("context_var2")c2.set("val2") # 此时得到的是所有 ContextVar 对象和设置的值之间的映射# 它实现了 collections.abc.Mapping 接口# 因此我们可以像操作字典一样操作它context = contextvars.copy_context()# key 就是对应的 ContextVar 对象,value 就是设置的值print(context[c1]) # val1print(context[c2]) # val2for ctx, value in context.items(): print(ctx.get(), ctx.name, value) """ val1 context_var1 val1 val2 context_var2 val2 """ print(len(context)) # 2除此之外,context 还有一个 run 方法:1234567891011121314151617181920212223242526272829import contextvars c1 = contextvars.ContextVar("context_var1")c1.set("val1")c2 = contextvars.ContextVar("context_var2")c2.set("val2") context = contextvars.copy_context() def change(val1, val2): c1.set(val1) c2.set(val2) print(c1.get(), context[c1]) print(c2.get(), context[c2]) # 在 change 函数内部,重新设置值# 然后里面打印的也是新设置的值context.run(change, "VAL1", "VAL2")"""VAL1 VAL1VAL2 VAL2""" print(c1.get(), context[c1])print(c2.get(), context[c2])"""val1 VAL1val2 VAL2"""我们看到 run 方法接收一个 callable,如果在里面修改了 ContextVar 实例设置的值,那么对于 ContextVar 而言只会在函数内部生效,一旦出了函数,那么还是原来的值。但是对于 Context 而言,它是会受到影响的,即便出了函数,也是新设置的值,因为它直接把内部的字典给修改了。小结以上就是 contextvars 模块的用法,在多个协程之间传递数据是非常方便的,并且也是并发安全的。如果你用过 Go 的话,你应该会发现和 Go 在 1.7 版本引入的 context 模块比较相似,当然 Go 的 context 模块功能要更强大一些,除了可以传递数据之外,对多个 goroutine 的级联管理也提供了非常清蒸的解决方案。总之对于 contextvars 而言,它传递的数据应该是多个协程之间需要共享的数据,像 cookie, session, token 之类的,比如上游接收了一个 token,然后不断地向下透传。但是不要把本应该作为函数参数的数据,也通过 contextvars 来传递,这样就有点本末倒置了。
-
整理 | 苏宓出品 | CSDN(ID:CSDNnews)近日,来自多伦多大学和 YScope 公司(为软件系统提供创新的日志管理和故障排除工具。由一群计算机工程教授和博士创立)的 David Lion、多伦多大学 Adrian Chiu 和 Michael Stumm、多伦多大学和 YScope 公司 Ding Yuan 共同发布了一份《调查托管语言的运行时性能:为什么 JavaScript 和 Python 比 C++ 慢了 8 倍和 29 倍,而 Java 和 Go 却能更快》(https://www.usenix.org/system/files/atc22-lion.pdf)的论文分析报告,深度剖析了不同编程语言运行时在代码开发中真实的性能情况,由此方便开发者可以精确地测量执行任何字节码指令所花费的时间等。性能是系统软件不得不面对的挑战在报告中,研究人员指出,自 2015 年以来,具有集成运行时环境的编程语言越来越受欢迎,其中,全球知名的代码托管平台 GitHub 上最受欢迎的三种语言分别是 JavaScript、Java 和 Python。作为开发利器,编程语言帮助开发者快速构建各种应用程序和服务,也极大地提高了生产力。同时,这些语言自身也提供了各种功能,如动态类型检查、带有垃圾收集的内存管理,以及动态内存安全检查等等。为此,研究人员用「托管语言」(managed languages)专业术语来指代这些类型的编程语言。现实来看,托管语言越来越多地被用于实现性能至关重要的系统软件上,如Hadoop 和 Spark 都在 Java 虚拟机(JVM)上运行,因为它们分别用 Java 和 Scala 实现;Kubernetes、etcd(分布式键值存储)和 M3(由 Uber 建立的分布式时间序列数据库和查询引擎)都是用 Go 实现的。当前,甚至连操作系统(OS)的内核 Biscuit 也是用 Go 实现的 。Openstack、Paypal、Instagram 和 Dropbox 都大量使用 Python,其中,Python 是 Dropbox "在后台服务和桌面客户端应用中使用最广泛的语言",在一个存储库中就有近 400 万行 Python 代码;JavaScript 也被用于 Facebook 的 Bladerunner pub/sub 系统的性能关键路径中。在开发过程中,编程语言的性能在一开始很少会被考虑到项目中,部分原因是不少开发者认为性能问题可以在以后慢慢去解决,也许可以通过简单地增加硬件来进行横向扩展。不过,随着代码产品或服务使用规模的扩大,服务变得越来越慢或者硬件成本变高,性能成为一个不容忽视的问题。这也是为什么 Stream 要放弃了 Python 而改用 Go、 Discord 从 Go 切换到 Rust、Twitter 从 Ruby on Rails 切换到 Scala 和 Java 的主要原因。不少开发者往往为了提升性能,想破脑袋,但现实只有两条路,一条是从现有的代码中想尽办法尽可能地做优化,另一条是思考使用的编程语言是否已经达到了性能极限,看看有没有必要将旧的代码移植到一个新的性能更高的语言上。为了彻底解开系统软件中不同编程语言导致的性能问题,研究人员决定以 C++ 为极限,对 Java、Go、JavaScript 和 Python 四种编程,还有应用最广泛的运行时系统 CPython、OpenJDK。Node.js 与 JavaScript 的 V8 引擎进行深入的定量性能分析。同时,研究人员还从头开始建立了 6 个应用程序,并创建了一个名为 LangBench 基准(https://github.com/topics/langbench)。这些应用程序涵盖了各种不同的计算强度、内存使用、网络和磁盘 I/O 强度以及可用的并发性的应用场景等复杂性。对此,研究人员全面分析了它们的完成时间、资源使用和可扩展性。测试方法值得一提的是,研究人员指出,这份论文没有也不可能全面地回答与语言运行时的性能有关的每一个问题。本文只是评估了四种语言的运行时,而且对于每种语言,只评估了最广泛使用的实现。此外,研究人员只在一个单一的操作系统/硬件堆栈上运行了工作负载。其研究结果与使用的基准有关,这些基准模拟了现实生活中的应用,但可能不代表广泛的应用。在测试方法上,研究人员在两台内部服务器上进行了实验,每台服务器有 2 个Xeon E5-2630V3、16 个虚拟核心、2.4GHz CPU、256GB DDR4 内存和两个 7200 RPM 硬盘。它们运行的系统是 Linux 4.15.0,并通过 10Gbps 的互联网络连接。对于 C++ 程序,研究人员使用的是 GCC 9.3.0 根据 C++17 标准用 -O3 进行编译。对于 OpenJDK 13、CPython 3.8.1 和Go 1.14.1 ,其使用了各自语言的参考实现。同时,使用 Node.js 13.12.0 和 V8 7.9.317.25 版本。研究人员对每个基准进行了 5 次测试,取平均值。其中,在运行键-值存储、日志分析器和文件服务器的基准时,client 和 worker 线程的数量从 1 到 1024 不等。对于 OpenJDK 和 V8 来说,最小的内存量是通过确定不会导致崩溃的第一个堆配置来设置的;对于 Go 来说,GOGC 被设置为5%。然后研究人员不断增加堆的设置,直到性能不再提高。其使用第一个设置的结果(即最小的堆大小)得出最佳性能。对于日志解析器和文件服务器基准,所用的日志文件被存储在一个复制系数为 2 的分布式文件系统上。 在运行每个基准之前,研究人员都清除了 Linux 的页面缓存,以保证测试准确性。其中,优化的 GCC 平均速度最快,Go 和 OpenJDK 紧随其后,比 GCC 慢了 1.30 倍和 1.43 倍。令人印象深刻的是,在 12 项基准测试中,Go 和 OpenJDK 有 3 项超过了优化的 GCC。总体而言,研究人员发现 V8 / Node.js 和 CPython 表现最差,执行应用程序的平均速度分别比 C++ 应用程序慢 8.01 倍和 29.50 倍,这意味着运行时下,JavaScript、Python 要比 C++ 慢这么多。更糟糕的是,这两个运行时上的应用程序扩展性很差,因为它们无法有效地利用多个内核。在极端情况下,CPython 比 GCC 慢了 129.66 倍(在排序基准中)。V8/Node.js 和 CPython 只有在工作负载受到磁盘 I/O 的瓶颈时,即在文件服务器基准中,才与 GCC 有竞争力。相比之下,OpenJDK 和 Go 应用程序即 Java 和 Go 语言比 C++ 更具有性能竞争力,运行速度仅慢了 1.43 倍和 1.30 倍,并且可以轻松扩展到多个内核。在一些应用中,OpenJDK 和 Go 的性能超过了 C++ 的同类产品。更多完整报告内容详见:https://www.usenix.org/system/files/atc22-lion.pdf
-
python使用SAX解析xmlSAX是一种基于事件驱动的 API。利用SAX解析XML文档牵涉到两个部分: 解析器和事件处理器。解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件。而事件处理器则负责对事件作出响应,对传递的XML数据进行处理。<psax适于处理下面的问题:< p="">1、对大型文件进行处理;2、只需要文件的部分内容,或者只需从文件中得到特定信息。3、想建立自己的对象模型的时候。在python中使用sax方式处理xml要先引入xml.sax中的parse函数,还有xml.sax.handler中的ContentHandler。ContentHandler类方法介绍characters(content)方法调用时机:从行开始,遇到标签之前,存在字符,content 的值为这些字符串。从一个标签,遇到下一个标签之前, 存在字符,content 的值为这些字符串。从一个标签,遇到行结束符之前,存在字符,content 的值为这些字符串。标签可以是开始标签,也可以是结束标签。startDocument() 方法文档启动的时候调用。endDocument() 方法解析器到达文档结尾时调用。startElement(name, attrs)方法遇到XML开始标签时调用,name是标签的名字,attrs是标签的属性值字典。endElement(name) 方法遇到XML结束标签时调用。</psax适于处理下面的问题:<>
-
Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。Python 本身也是由诸多其他语言发展而来的,这包括 ABC、Modula-3、C、C++、Algol-68、SmallTalk、Unix shell 和其他的脚本语言等等。像 Perl 语言一样,Python 源代码同样遵循 GPL(GNU General Public License)协议。现在 Python 是由一个核心开发团队在维护,Guido van Rossum 仍然占据着至关重要的作用,指导其进展。Python 2.7 被确定为最后一个 Python 2.x 版本,它除了支持 Python 2.x 语法外,还支持部分 Python 3.1 语法。Python 特点 1.易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。 2.易于阅读:Python代码定义的更清晰。 3.易于维护:Python的成功在于它的源代码是相当容易维护的。 4.一个广泛的标准库:Python的最大的优势之一是丰富的库,跨平台的,在UNIX,Windows和Macintosh兼容很好。 5.互动模式:互动模式的支持,您可以从终端输入执行代码并获得结果的语言,互动的测试和调试代码片断。 6.可移植:基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。 7.可扩展:如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。 8.数据库:Python提供所有主要的商业数据库的接口。 9.GUI编程:Python支持GUI可以创建和移植到许多系统调用。 10.可嵌入: 你可以将Python嵌入到C/C++程序,让你的程序的用户获得"脚本化"的能力。
-
Python 变量类型变量是存储在内存中的值,这就意味着在创建变量时会在内存中开辟一个空间。基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。因此,变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符。Python 中的变量赋值不需要类型声明。每个变量在内存中创建,都包括变量的标识,名称和数据这些信息。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。等号 = 用来给变量赋值。等号 = 运算符左边是一个变量名,等号 = 运算符右边是存储在变量中的值。例如:#!/usr/bin/python# -*- coding: UTF-8 -*- counter = 100 # 赋值整型变量miles = 1000.0 # 浮点型name = "John" # 字符串 print counterprint milesprint name多个变量赋值Python允许你同时为多个变量赋值a = b = c = 1标准数据类型Python 定义了一些标准类型,用于存储各种类型的数据。Python有五个标准的数据类型: Numbers(数字) String(字符串) List(列表) Tuple(元组) Dictionary(字典)Python 数字数字数据类型用于存储数值。他们是不可改变的数据类型,这意味着改变数字数据类型会分配一个新的对象。当你指定一个值时,Number 对象就会被创建
-
当前有以下需求: 实现一个用于计算(包括加减乘除)的小程序: 接受用户输入的计算式(如:3*4+1.1); 计算值; 将计算过程中涉及到的符号存储在元组中; 将计算中涉及到的值去重后存在列表里面(由大到小排列); 最后将计算过程存在字典里面(如 {"3*4+1.1":13.1} ) 本小白的代码是:numbers_count = input("请输入计算式:")print("您的结果为:", eval(numbers_count))symbol = {"+", "-", "*", "/", "%", "//", "**"}symbol_count = set({})for i in numbers_count: if i in symbol: symbol_count.add(i)symbol_tuple = tuple(symbol_count)print(symbol_tuple)numbers_all = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}numbers = set({})for i in numbers_count: if i in numbers_all: numbers.add(i)numbers_list = list(numbers)numbers_list.sort(reverse=True)print(numbers_list)numbers_dic = {numbers_count : eval(numbers_count)}print(numbers_dic)输入:5*3+3/4-3-4-5输出结果:请输入计算式:5*3+3/4-3-4-5您的结果为: 3.75('/', '*', '+', '-')['5', '4', '3']{'5*3+3/4-3-4-5': 3.75}本小白的思路是:1.为达成接受用户输入的计算式,需使用input函数2.为计算用户输入值,需使用eval 函数3.为计算过程中涉及到的符号存储在元组中,先创建一个包含所有符号的集合和一个空的集合。通过for和if,遍历用户输入的计算式的每个元素,并判断其是否在包含所有符号的集合中4.为计算中涉及到的值去重后存在列表里面(由大到小排列),与上面的方法类似。但是需额外使用numbers_list.sort(reverse=True),对数字进行排序&为了实现去重,可以将出现的数字放入集合中,最后再将集合转为列表。5为计算过程存在字典里面,需创建字典。作者使用的是x = {'a':'A','b':"B",'c':3}方法,除此之外课上还教了X = dict(a='A',b="B", c=3)和x = dict([("a", "A"),("b", "B"),("c",3)])方法。代码说明:numbers_count = input("请输入计算式:")#获得用户输入值print("您的结果为:", eval(numbers_count))#通过eval函数直接算出结果symbol = {"+", "-", "*", "/", "%", "//", "**"}#创建包含所有符号的集合symbol_count = set({})for i in numbers_count: if i in symbol: symbol_count.add(i)#以上为遍历+判断symbol_tuple = tuple(symbol_count)#转为元组类型print(symbol_tuple)numbers_all = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}numbers = set({})for i in numbers_count: if i in numbers_all: numbers.add(i)numbers_list = list(numbers)numbers_list.sort(reverse=True)#使用sort函数进行排序print(numbers_list)numbers_dic = {numbers_count : eval(numbers_count)}print(numbers_dic)写在最后,作者本科商科,从未碰过代码。因,研究生专业为区块链技术,不得已开始自学编程。以上帖子仅为作者对该任务的一些粗浅认识,如果其中文字或者代码有误,希望各位大神/大佬/前辈不吝赐教,谢谢!!!
-
正则表达式实现重叠匹配正则表达式与正则匹配正则表达式正则匹配正则表达式实现重叠匹配1234import regexstring = '100101010001'str_re = '101'print(regex.findall(str_re, string, overlapped=True))普通的re库匹配,只能匹配一个’101’。正则表达式与正则匹配正则表达式正则表达式可理解为对数据筛选的表达式,是有限个原子和元字符组成。原子:基本组成单位,每个表达式至少有一个原子普通字符组成原子 非打印字符组成原子(不打印在输出台的字符)\n:换行\t:tab退格符通用字符组成原子\w:匹配任意字母、数字、下划线\W:与\w相反\d:匹配任意十进制数\D:与\d相反\s:匹配任意空白字符,如空格、换行、缩进\S:与\s相反原子表组成原子一组原子组成一个表,由[]声明表内原子优先级相等,但内容只出现依次若原子表以 ^ 开头,则表示取反12345678910111213141516#普通字符组成原子pat1 = "abcd" #非打印字符组成原子pat2 = "\n" #通用字符做原子pat3 = "\w" #原子表组成原子pat4 = "py[abc]"#可以匹配pya,pyb,pyc,但匹配pyab等原子表重复出现的情况失败 #原子表开头带 ^ 表示取反pat5 = "py[^abc]"#第三个位置匹配除了a,b,c外的任意一个字符元字符:正则表达式中具有特殊含义的字符.匹配任意字符,除了换行符^匹配字符串开始的位置$匹配字符串结束的位置,当出现多组符合的匹配时,返回字符串最后的那组匹配*匹配 0,1,n 次前面的原子【贪婪模式:尽可能多的匹配】?匹配 0,1 次前面的原子【懒惰模式:精确匹配】+匹配 1,n 次前面的原子{ j }前面的原子出现 j 次{ j , }前面的原子至少出现 j 次{ j , k }前面的原子至少出现 j 次,至多出现 k 次i | j匹配 i 或 j ,若 i 与 j 同时出现,匹配 i( )组,限制这组数据的组合如()内所描述一样,只返回符合括号内描述的内容模式修正符即函数中 flag 位置的参数,在不改变正则表达式的情况下改变其含义,调整匹配结果。re.I匹配时忽略大小写re.M多行匹配re.L本地化识别匹配re.U根据unicon字符匹配,影响\w \Wre.S匹配包括换行符正则匹配正则表达式是对字符串进行模糊匹配,其中一个应用为正则匹配。正则匹配是python爬虫的一个使用技术,用于在爬取的文本信息中提取目标信息。正则匹配常用的函数:(调用正则表达式模块re)re.search(pat, str[, flag]):扫描字符串str,返回pat的位置(第一次成功匹配的),flag用于控制正则表达式的匹配方式1234import restr = 'python'pat = 'pytho[a-n]'print(re.search(pat, str))re.match(pat, str[, flag]):扫描字符串str开始的位置,返回pat的位置(第一次成功匹配的),flag用于控制正则表达式的匹配方式【若开始就不符合则结束,返回none】123456import restr_1 = 'hello world'str_2 = 'world hello'pat = 'world'print(re.match(pat, str_1))print(re.match(pat, str_2))re.complie(pat[, flag]):编译正则表达式pat,返回正则表达式对象findall(str[, pos[, endpos]]):匹配所有,用列表返回string中所有匹配到的子串【不止第一次】,pos和endpos可指定在string中的起始位置re.complie(pat).findall(str):全局匹配函数,匹配str中所有符合pat的子串,装入一个列表返回结果12345import restr = "hello world hello world hello world"pat = "hello"print(re.complie(pat).findall(str))print(re.complie(pat).findall(str, 5, 15))re.sub(pat, repl, str[, count[, flag]]):替换字符串中的匹配项【清洗数据】,可用count指定最大替换次数12345import restr = "400-823-823"pat = "-"#短横改空格,最大替换次数2str_new = re.sub(pat, " ", str, count=2)
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第五期2026/09/04 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;蒋春阳-华为云AI开发者案例开发专家
本期直播内容: AI工具体验营 · 第5-8课连讲。Agent-Team 多智能体协作完成毕业设计实践
回顾中 -
华为云开发者AI素养ClassRoom·第六期2026/09/08 周二 19:00-20:00
樊渊-2026华为软件挑战赛冠军
高手来了:看软挑高手解析二维排样问题—从工业难题到算法突破
回顾中
热门标签