基础语法
1.变量的命名
- 如果设计多个单词,建议使用creat_sutdent,而不是CreatStudent,虽然后者也合法,但是不建议
- 如果名字过长,可以缩写,然后在后面进行注释,如:ymd = “2021-11-3” #ymd is year_month_day
- 多个一起赋值a, b, c = 2, 4, 5,等号左右数量要对应
2.字符串
-
id()方法可以查询变量的地址
-
dir()方法可以查询变量的所有方法
-
encode可以进行编码解码,将字符串转为bytes
-
引号和双引号的配合使用:
new_str = "hello world "" hhhhhh" #不会报错 new_str = "hello "world" hhhhhh" #会报错,这种引号里夹引号的要用不同的引号"hello 'world' hhhhhh" print(new_str)
-
字符串的max和min函数可以取出当前数据中的最大最小值
print(max(“今天是11月3日!”)) #返回月 -
字符串的capitalize方法,capitalize()将字符串的第一个字母变成大写,其他字母变小写。
str = "this is string example from runoob....wow!!!" print ("str.capitalize() : ", str.capitalize()) #结果:str.capitalize() : This is string example from runoob....wow!!!
3.列表
-
列表对 + 和 * 的操作符与字符串相似。+ 号用于组合列表,* 号用于重复列表。
print([1, 2, 3] + [4, 5, 6]) #结果是[1, 2, 3, 4, 5, 6],将两个列表组合 print(['Hi!'] * 4) #结果是 ['Hi!', 'Hi!', 'Hi!', 'Hi!'],将['Hi!']列表乘以4次
-
对于python列表+= 和+操作的一些区别:
#方式一:+=的操作 >>> a = [] >>> a += "a" >>> a ["a"]
#方式二:+的操作 >>> a = [] >>> a = a + "a" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "str") to list >>>
原因:
python中列表的+=相当于执行的是extend(后续会讲extend和append的区别)操作,在上述代码中也就是将右侧的字符串"a"添加到了变量a中,所以命令行下调用a时,显示的是有一个值为"a"的列表;
但是对列表执行+操作时,操作数必须是列表,所以就出现了上面的情况。而且+操作会生成一个新的对象,而+=还是操作的原来的对象,如:#方式一:+=的操作,列表前后地址相同,所以+=还是操作的原来的对象 >>> a = [1] >>> id(a) 2535493303944 >>> a += [2,3,4] >>> a [1, 2, 3, 4] >>> id(a) 2535493303944
#方式二:+的操作,列表前后地址变化了,所以+操作会生成一个新的对象 >>> a = [1] >>> id(a) 2535493303944 >>> a = a + [2,3,4] >>> a [1, 2, 3, 4] >>> id(a) 2535493303752
-
对于extend (扩展) 与 append (追加)与insert (插入)的区别:
# extend (扩展) 操作: >>> li = ['a', 'b', 'c'] >>> li.extend(['d', 'e', 'f']) >>> li ['a', 'b', 'c', 'd', 'e', 'f'] >>> len(li) 6 >>> li[-1] 'f' # append (追加)操作: >>> li = ['a', 'b', 'c'] >>> li.append(['d', 'e', 'f']) >>> li ['a', 'b', 'c', ['d', 'e', 'f']] >>> len(li) 4 >>> li[-1] ['d', 'e', 'f'] # insert (插入)操作: >>> li = ['a', 'b', 'c'] >>> li.insert(1, 'd') >>> li ['a', 'd', 'b', 'c'] >>> li.insert(1, ['d','gggg']) >>> li ['a', ['d', 'gggg'], 'd', 'b', 'c']
Lists 的两个方法 extend 和 append 看起来类似,但实际上完全不同。
extend 接受一个参数,这个参数总是一个 list(extend只能接收列表),并且把这个 list 中的每个元素添加到原 list 中。
append可以接收任何数据类型的参数,并将参数作为一个元素添加到原来的list。
insert是将元素添加到指定index下,需要两个参数。 -
对于copy()和=赋值的区别:
a = [1, 2, 3] b = a.copy() c = a a.append(4) print(a) # [1, 2, 3, 4] print(b) # [1, 2, 3] print(c) # [1, 2, 3, 4] #原因:赋值的变量与原始变量享有相同的内存空间(a和c地址相同),而copy函数是创建新的列表(a和b地址不同)
-
浅拷贝copy和深拷贝deepcopy:
#但是!!!!如果列表a中还有一层列表,情况就特殊点 #浅拷贝 a = [[1, 2, 3], [6, 7, 8]] b = a.copy() b[0].append(4) #对第二层元素进行操作 print(a) # [[1, 2, 3, 4], [6, 7, 8]] print(b) # [[1, 2, 3, 4], [6, 7, 8]] #深拷贝 import copy a = [[1, 2, 3], [6, 7, 8]] b = copy.deepcopy(a) #深拷贝的调用必须导入copy模块使用,浅拷贝可以b = copy.copy(a),也可以a.copy() b[0].append(4) print(a) #[[1, 2, 3], [6, 7, 8]] print(b) #[[1, 2, 3, 4], [6, 7, 8]]
结论:深拷贝:与原始变量内存空间不相同,不共享数据。浅拷贝:浅拷贝是对最外层的数据创建一个新的内存空间来存储,而对内层的数据内存地址进行引用。
4.字典
- dict.keys()函数以列表返回一个字典所有的键。
#dict.keys()是获取字典所有的key并返回一个伪列表,如果需要对列表进行操作需要用list()转换使用 dict = {'Name': 'Zara', 'Age': 7} print("Value : %s" % dict.keys()) #Value : ['Age', 'Name'] print(type(dic.keys())) #<class 'dict_keys'>
- dict.values()函数以列表返回一个字典所有的value。
#dict.values()是获取字典所有的value并返回一个伪列表,如果需要对列表进行操作需要用list()转换使用 dict = {'Name': 'Zara', 'Age': 7} print("Value : %s" % dict.values()) #Value : dict_values(['Zara', 7]) print(type(dict.values())) #<class 'dict_values'>
- 对于字典value的访问,不建议使用dict[“key”],这样如果没有key会直接报错,
因此建议使用dict.get(“key”)来操作,这样如果没有key不会报错,会返回None。#dict.get(key, default=None) dict = {'Name': 'Runoob', 'Age': 27} print("Value : %s" % dict.get('Age')) print("Value : %s" % dict.get('Sex', "Not Available")) print("Value : %s" % dict.get('Apple')) #key -- 字典中要查找的键。default -- 如果指定键的值不存在时,返回该默认值。 #返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。 #Value : 27 #Value : Not Available #Value : None
流程控制与函数
1.集合常用方法
-
集合的定义:集合(set)是一个无序的不重复元素序列,常用来对两个列表进行交并差的处理
-
集合相关操作:
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} >>> print(basket) # 这里演示的是去重功能 {'orange', 'banana', 'pear', 'apple'} >>> 'orange' in basket # 快速判断元素是否在集合内 True >>> 'crabgrass' in basket False >>> # 下面展示两个集合间的运算. !!!必须两个都是集合!!! ... >>> a = set('abracadabra') >>> b = set('alacazam') >>> a {'a', 'r', 'b', 'c', 'd'} >>> a - b # 集合a中包含而集合b中不包含的元素 {'r', 'd', 'b'} >>> a | b # 集合a或b中包含的所有元素 {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'} >>> a & b # 集合a和b中都包含了的元素 {'a', 'c'} >>> a ^ b # 不同时包含于a和b的元素 {'r', 'd', 'b', 'm', 'z', 'l'}
-
添加元素
#add添加方法 s.add(x) #将元素 x 添加到集合 s 中,如果元素已存在,则不进行任何操作。 >>> thisset = set(("Google", "Runoob", "Taobao")) >>> thisset.add("Facebook") >>> print(thisset) {'Taobao', 'Facebook', 'Google', 'Runoob'} #update方法 s.update(x) #x 可以有多个,用逗号分开。 >>> thisset = set(("Google", "Runoob", "Taobao")) >>> thisset.update({1,3}) >>> print(thisset) {1, 3, 'Google', 'Taobao', 'Runoob'} >>> thisset.update([1,4],[5,6]) >>> print(thisset) {1, 3, 4, 5, 6, 'Google', 'Taobao', 'Runoob'} >>>
-
删除元素
#remove方法 s.remove(x) #将元素 x 从集合 s 中移除,如果元素不存在,则会发生错误。 >>> thisset = set(("Google", "Runoob", "Taobao")) >>> thisset.remove("Taobao") >>> print(thisset) {'Google', 'Runoob'} >>> thisset.remove("Facebook") # 不存在会发生错误 Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'Facebook' >>> #discard方法 s.discard(x) #如果元素不存在,不会发生错误 >>> thisset = set(("Google", "Runoob", "Taobao")) >>> thisset.discard("Facebook") # 不存在不会发生错误 >>> print(thisset) {'Taobao', 'Google', 'Runoob'} #pop方法 s.pop() #随机删除集合中的一个元素 thisset = set(("Google", "Runoob", "Taobao", "Facebook")) x = thisset.pop() print(x) #x为Runoob(被pop出来的元素) #多次执行测试结果都不一样。 #set 集合的 pop 方法会对集合进行无序的排列,然后将这个无序排列集合的左面第一个元素进行删除。
-
求差集
#set.difference(set) x = {"apple", "banana", "cherry"} y = {"google", "microsoft", "apple"} #返回一个集合,元素包含在集合x,但不在集合y z = x.difference(y) print(z) #{'cherry', 'banana'}
-
求交集
#set.intersection(set1, set2 ... etc) 可以多个参数 #set1 -- 必需,要查找相同元素的集合 #set2 -- 可选,其他要查找相同元素的集合,可以多个,多个使用逗号 , 隔开 x = {"apple", "banana", "cherry"} y = {"google", "runoob", "apple"} a = {"hello", "apple", "google"} #返回一个新集合,该集合的元素既包含在集合x又包含在集合y和a中 z = x.intersection(y, a) print(z) #{'apple', 'google'}
-
求并集
#set.union(set1, set2...) 可以多个参数 #set1 -- 必需,合并的目标集合 #set2 -- 可选,其他要合并的集合,可以多个,多个使用逗号 , 隔开。 x = {"apple", "banana", "cherry"} y = {"google", "runoob", "apple"} a = {"hello", "runoob", "Google"} z = x.union(y, a) #合并多个集合,重复元素只会出现一次: print(z) #{'cherry', 'apple', 'runoob', 'banana', 'Google', 'hello', 'google'}
-
是否包含
#set.isdisjoint(set) x = {"apple", "banana", "cherry"} y = {"google", "runoob", "facebook"} #判断集合y中是否有包含集合x的元素 #返回布尔值,如果不包含返回 True,否则返回 False。 z = x.isdisjoint(y) print(z) #True 因此y中不包含x
2.for循环
-
使用for循环删除列表中的偶数
list_1 = [3, 6, 8, 9, 25, 36, 100, 105] for item in list_1: if(item % 2 == 0): list_1.remove(item) print(list_1) #result:[3, 8, 9, 25, 100, 105] #结果中却还有偶数,因为remove删掉一个元素后,后面的元素会自动覆盖到前一个位置,因此6后面的8就逃过一劫,36后面的100同理
如何解决呢?
list_1 = [3, 6, 8, 9, 25, 36, 100, 105] for item in list_1[::-1]: if(item % 2 == 0): list_1.remove(item) print(list_1) #result:[3, 9, 25, 105] #逆序遍历即可。逆序表示为list[::-1]
3.while循环
-
推导式 难点
# 列表推导式,20以内所有偶数的平方 print([x ** 2 for x in range(20) if x % 2 == 0]) # 运行结果:[0, 4, 16, 36, 64, 100, 144, 196, 256, 324] # 元组推导式 print(tuple(x for x in range(10))) # 结果是生成器对象,使用tuple()函数将其转换为元组 # 运行结果:(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) # 字典推导式 print({x:x**2 for x in range(10)}) # 运行结果:{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} # 集合推导式 print({x for x in range(10)}) # 运行结果:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} #一行实现99乘法表 print("\n".join("\t".join(["%s*%s=%s" % (x, y, x * y) for y in range(1, x + 1)]) for x in range(1, 10)))
4.函数
-
函数的参数
#*args与**kwargs是可变类型且*args将参数封装成元组类型,**kwargs将参数封装成字典类型 def seq(num, num1, num2): if num < 88: return num1 * num2 else: return num1 + num2 if __name__ == '__main__': # 定义变量tuple1的值为元组(5,2,1) tuple1 = (5,2,1) # *******使用*tuple1可实现对元组tuple1对解包****** print(*tuple1) #result: 5 2 1 # 调用函数,传入参数tuple1,并打印函数返回值 print(seq(*tuple1)) #相当于print(seq(5, 2, 1))
-
综合例子,特别注意!!
# 带一个*的是元组,带两个星号的是字典。 def total(a=5, *tuple_num, **dic_id): print(type(tuple_num), tuple_num) print(type(dic_id), dic_id) print('a', a) for single_item in tuple_num: print('single_item', single_item) for key, value in dic_id.items(): print(key, value) total(10, 1, 2, 3, Jack=1123, John=2231, Inge=1560) """result: <class 'tuple'> (1, 2, 3) <class 'dict'> {'Jack': 1123, 'John': 2231, 'Inge': 1560} a 10 single_item 1 single_item 2 single_item 3 Jack 1123 John 2231 Inge 1560 """
-
函数定义的高级用法(python3.7后才加入)
def run(name: str, age: int = 33) -> str: """ 表示name参数类型为字符串,age参数类型为整型,返回值类型为字符串 """ print(name, age) return name run("zhsngsan", 22)
-
全局变量和局部变量
局部变量:
1.定义在函数内部的变量称为局部变量(函数的形参也是局部变量) 2.局部变量只能在函数内部使用
3.局部变量在函数调用时才能够被创建,在函数调用之后会自动销毁
全局变量:
1.定义在函数外部,模块内部的变量称为全局变量 2.全局变量,所有函数都可以直接访问(但函数内部不能直接修改全局变量的绑定关系)
面向对象
1.对象的基础
-
对象的生命周期
class class_name(object): # 可以在pass里添加动作,达到重写方法的作用 def __init__(self): # 这个是构造函数,具有初始化的作用,也就是当该类被实例化的时候就会执行该函数 pass def __del__(self): # 这个是析构函数,当使用del删除对象时,会调用他本身的析构函数,另外当对象在某个作用域中调用完毕,在跳出其作用域的同时析构函数也会被调用一次,这样可以用来释放内存空间。 pass
-
私有函数和变量
# 1、 _xx 以单下划线开头的表示的是protected类型的变量。即保护类型只能允许其本身与子类进行访问。若内部变量标示,如: 当使用“from M import”时,不会将以一个下划线开头的对象引入 。 # 2、 __xx 双下划线的表示的是私有类型的变量。只能允许这个类本身进行访问了,连子类也不可以用于命名一个类属性(类变量),调用时名字被改变(在类FooBar内部,__boo变成_FooBar__boo,如self._FooBar__boo) # 3、 __xx__定义的是特列方法。用户控制的命名空间内的变量或是属性,如init , __import__或是file 。只有当文档有说明时使用,不要自己定义这类变量。 (就是说这些是python内部定义的变量名)
class Cat(object): __cat_type = 'cat' # 创建私有属性 def __init__(self, name, sex): self.name = name self.__sex = sex # 构造函数内创建私有属性 def run(self): result = self.__run() print(result) def __run(self): return f'{self.__cat_type} 小猫 {self.name} {self.__sex}开心地奔跑着' def jump(self): result = self.__jump() print(result) def __jump(self): return f'{self.__cat_type} 小猫 {self.name} {self.__sex} 开心地跳着' cat = Cat(name='花生', sex='boy') cat.run() cat.jump() # cat.__run() # 会报错 print(dir(cat)) print(cat._Cat__jump()) # 这样可以调用私有函数 # print(cat.__sex) # 会报错 print(cat._Cat__sex) """ result cat 小猫 花生 boy开心地奔跑着 cat 小猫 花生 boy 开心地跳着 ['_Cat__cat_type', '_Cat__jump', '_Cat__run', '_Cat__sex', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'jump', 'name', 'run'] cat 小猫 花生 boy 开心地跳着 boy """
2.装饰器的基本概念
-
装饰器的概念
理解装饰器的前提**:1.所有东西都是对象(函数可以当做对象传递) 2.闭包**
闭包的概念:
1)函数嵌套
2)内部函数使用外部函数的变量
3)外部函数的返回值为内部函数下面写一个最为简单的闭包的例子:
def test(name): def test_in(): print(name) return test_in func = test('whyz') func()
装饰器的原型:
import time def showtime(func): def wrapper(): start_time = time.time() func() end_time = time.time() print('spend is {}'.format(end_time - start_time)) return wrapper def foo(): print('foo..') time.sleep(3) foo = showtime(foo) foo()
不带参数的装饰器:(装饰器,被装饰函数都不带参数)
import time def showtime(func): def wrapper(): start_time = time.time() func() end_time = time.time() print('spend is {}'.format(end_time - start_time)) return wrapper @showtime #foo = showtime(foo) def foo(): print('foo..') time.sleep(3) @showtime #doo = showtime(doo) def doo(): print('doo..') time.sleep(2) foo() doo()
带参数的被装饰的函数
import time def showtime(func): def wrapper(a, b): start_time = time.time() func(a,b) end_time = time.time() print('spend is {}'.format(end_time - start_time)) return wrapper @showtime #add = showtime(add) def add(a, b): print(a+b) time.sleep(1) @showtime #sub = showtime(sub) def sub(a,b): print(a-b) time.sleep(1) add(5,4) sub(3,2)
带参数的装饰器(装饰函数),
实际是对原有装饰器的一个函数的封装,并返回一个装饰器(一个含有参数的闭包函数),
当使用@time_logger(3)调用的时候,Python能发现这一层封装,并将参数传递到装饰器的环境去1 import time 2 def time_logger(flag = 0): 3 def showtime(func): 4 def wrapper(a, b): 5 start_time = time.time() 6 func(a,b) 7 end_time = time.time() 8 print('spend is {}'.format(end_time - start_time)) 9 10 if flag: 11 print('将此操作保留至日志') 12 13 return wrapper 14 15 return showtime 16 17 @time_logger(2) #得到闭包函数showtime,add = showtime(add) 18 def add(a, b): 19 print(a+b) 20 time.sleep(1) 21 22 add(3,4)
类装饰器:一般依靠类内部的__call__方法
1 import time 2 class Foo(object): 3 def __init__(self, func): 4 self._func = func 5 6 def __call__(self): 7 start_time = time.time() 8 self._func() 9 end_time = time.time() 10 print('spend is {}'.format(end_time - start_time)) 11 12 @Foo #bar = Foo(bar) 13 def bar(): 14 print('bar..') 15 time.sleep(2) 16 17 bar()
使用装饰器的缺点:
1.位置错误的代码->不要在装饰器之外添加逻辑功能
2.不能装饰@staticmethod 或者 @classmethod已经装饰过的方法
3.装饰器会对原函数的元信息进行更改,比如函数的docstring,name,参数列表:下面对装饰器第第三个缺点进行剖析,
1 import time 2 def showtime(func): 3 def wrapper(): 4 start_time = time.time() 5 func() 6 end_time = time.time() 7 print('spend is {}'.format(end_time - start_time)) 8 9 return wrapper 10 11 @showtime #foo = showtime(foo) 12 def foo(): 13 print('foo..') 14 time.sleep(3) 15 16 def doo(): 17 print('doo..') 18 time.sleep(2) 19 20 print(foo.__name__) 21 print(doo.__name__)
结果为:
wrapper doo
由此可以看出,装饰器会对原函数的元信息进行更改,可以使用wraps,进行原函数信息的添加
注解:wraps本身也是一个装饰器,他能把函数的元信息拷贝到装饰器函数中**使得装饰器函数与原函数有一样的元信息
以下是一个wraps的例子:
1 import time 2 from functools import wraps 3 def showtime(func): 4 5 @wraps(func) 6 def wrapper(): 7 start_time = time.time() 8 func() 9 end_time = time.time() 10 print('spend is {}'.format(end_time - start_time)) 11 12 return wrapper 13 14 @showtime #foo = showtime(foo) 15 def foo(): 16 print('foo..') 17 time.sleep(3) 18 19 def doo(): 20 print('doo..') 21 time.sleep(2) 22 23 print(foo.__name__) 24 print(doo.__name__)
结果为:
foo doo
常用的内置装饰器:1.staticmethod: 类似实现了静态方法 注入以后,可以直接 : 类名.方法
*2.property:经过property装饰过的函数 不再是一个函数,而是一个property,*类似实现get,set方法
1 @property 2 def width(self): 3 return self.__width 4 5 @width.setter 6 def width(self, newWidth): 7 self.__width = newWidth
3.classmethod: 与staticmethod很相似,貌似就只有这一点区别:
第一个参数需要是表示自身类的 cls 参数,
可以来调用类的属性,类的方法,实例化对象等。
3.常见的装饰器及功能
-
classmethod
将类函数可以不经过实例化而直接被调用(类方法)
class A(object): # 属性默认为类属性(可以给直接被类本身调用) num = "类属性" # 实例化方法(必须实例化类之后才能被调用) def func1(self): # self : 表示实例化类后的地址id print("func1") print(self) # 类方法,必须@classmethod和cls配套使用 @classmethod # 类方法(不需要实例化类就可以被类本身调用) def func2(cls): # cls : 表示没用被实例化的类本身,cls替代普通函数中的self print("func2") print(cls) print(cls.num) # 直接通过cls调用类属性和方法 cls().func1() # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准) def func3(): print("func3") print(A.num) # 属性是可以直接用类本身调用的 # A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的 A.func2() A.func3()
-
staticmethod
class A(object): def m1(self, n): print("self:", self) @classmethod def m2(cls, n): print("cls:", cls) @staticmethod def m3(n): pass a = A() a.m1(1) # self: <__main__.A object at 0x000001E596E41A90> A.m2(1) # cls: <class '__main__.A'> A.m3(1)
在类中一共定义了3个方法。
m1 是实例方法,第一个参数必须是 self(约定俗成的)。
m2 是类方法,第一个参数必须是cls(同样是约定俗成)。
m3 是静态方法,参数根据业务需求定,可有可无。当程序运行时,大概发生了这么几件事(结合下面的图来看)。
第一步:代码从第一行开始执行 class 命令,此时会创建一个类 A 对象(没错,类也是对象,一切皆对象嘛)同时初始化类里面的属性和方法,记住,此刻实例对象还没创建出来。
第二、三步:接着执行 a=A(),系统自动调用类的构造器,构造出实例对象 a
第四步:接着调用 a.m1(1) ,m1 是实例方法,内部会自动把实例对象传递给 self 参数进行绑定,也就是说, self 和 a 指向的都是同一个实例对象。
第五步:调用A.m2(1)时,python内部隐式地把类对象传递给 cls 参数,cls 和 A 都指向类对象。
4.继承
-
继承
继承是一种类间关系,描述一个类从另一个类获取成员信息的类间关系。
继承必定发生在两个类之间,参与继承关系的双方成为是父类和子类。
父类提供成员信息,子类获取成员信息。
子类可以添加父类没的成员
父类私有成员不可被继承
# 父类 class Father(object): def __init__(self, name, age): self.name = name self.age = age def hello(self): print("hello,武汉加油,我是%s" % self.name) # 子类继承父类· class Son(Father): def sing(self): print("我是{},我今年{}岁,我会唱歌".format(self.name, self.age)) self.hello() son1 = Son(name="小明", age=23) son1.sing() son1.hello() # 我是小明,我今年23岁,我会唱歌 # hello,武汉加油,我是小明 # hello,武汉加油,我是小明
5.多态
-
不同的子类对象调用相同的父类方法,产生不同的执行结果
子类重写父类的方法
# 先继承,再重构(重写) class Person(object): def __init__(self, name, age): self.name = name self.age = age self.sex = "normal" def talk(self): print("person is talking") class BlackPerson(Person): def __init__(self, name, age, strength): # 先继承,再重构(重写) Person.__init__(self, name, age) # 把子类实例传到父类(父类引用指向子类对象) print(self.name, self.age, self.sex) print(strength) def talk(self): # 相同的talk,父子执行的东西不同,就叫多态 print("BlackPerson is talking") def walk(self): print("BlackPerson is walking") b = BlackPerson("will smith", 30, "strong") b.talk() b.walk() # will smith 30 normal # strong # BlackPerson is talking # BlackPerson is walking
-
super()用法总结:
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题用super()而不是直接用类名调用父类方法的好处是不用管父类的名字。即使父类改名了,super()的调用依然有效
使用super()进行父类方法和属性的调用是个好习惯
# 单继承方式 class Animal(object): # 创建动物类 """动物类""" def __init__(self, name): # 初始化方法 self.name = name def eat(self): # 定义吃食物方法 print('%s正在吃食物' % self.name) def play(self): # 定义玩耍方法 print('%s正在玩' % self.name) def sleep(self): # 定义休息方法 print('%s正在休息' % self.name) class Dog(Animal): """狗类""" def __init__(self, name): super().__init__(name) # 定义基类的初始化方法 print('这是一只%s' % self.name) def eat(self): # 定义狗吃食物的方法 print('%s正在吃狗粮' % self.name) def bark(self): # 定义狗叫的方法 print('%s会汪汪叫' % self.name) dog = Dog('哈士奇') # 创建动物类的实例 dog.eat() # 调用吃食物的方法 dog.play() # 调用玩耍的方法 dog.bark() # 调用狗叫的方法 # 这是一只哈士奇 # 哈士奇正在吃狗粮 # 哈士奇正在玩 # 哈士奇会汪汪叫
# 多继承方式 class Food(object): """实物类""" def __init__(self, name): print('%s是食物' % name) class Vegetables(Food): """蔬菜类""" def __init__(self, Vname): print('%s是蔬菜' % Vname) super().__init__(Vname) class Fruit(Food): """水果类""" def __init__(self, Fname): print('%s是水果' % Fname) super().__init__(Fname) class Tomato(Fruit, Vegetables): """西红柿类""" def __init__(self): print('西红柿即是蔬菜又是水果') super().__init__('西红柿') print(Tomato.__mro__) # MRO 列表顺序,即Tomato类继承父类的顺序 tomato = Tomato() # 实例化西红柿类 # (<class '__main__.Tomato'>, <class '__main__.Fruit'>, <class '__main__.Vegetables'>, <class '__main__.Food'>, <class 'object'>) # 西红柿即是蔬菜又是水果 # 西红柿是水果 # 西红柿是蔬菜 # 西红柿是食物
6.类的高级函数
-
__str__
、__getattr__
、__setattr__
、__call__
函数class Test(object): def __str__(self): """ 如果定义了该函数,当print当前实例化对象的时候,会返回该函数的return信息 可用于定义当前类的描述信息""" return "这是这个类的描述信息" def __getattr__(self, item): """ 当调用的属性或者方法不存在时,会返回该方法定义的信息""" return "这个item:{}不存在".format(item) def __setattr__(self, key, value): """拦截当前类中不存在的属性与值""" self.__dict__[key] = value print(self.__dict__) def __call__(self, *args, **kwargs): """本质是将一个类变成一个函数""" print("call函数:{}被调用".format(*args)) t = Test() print(t) print(t.a) t.name = "tom" print(t.name) t("hello") """ 这是这个类的描述信息 这个item:a不存在 {'name': 'tom'} tom call函数:hello被调用 """
-
实现上述几个方法的链式操作
class Test2(object): def __init__(self, attr=''): self.__attr = attr def __call__(self, name): print('key is {}'.format(self.__attr)) return name def __getattr__(self, key): if self.__attr: key = '{}.{}'.format(self.__attr, key) else: key = key print("=======",key) return Test2(key) # 递归操作 t2 = Test2() print(t2.a.c('insane')) # ======= a # ======= a.c # key is a.c # insane
异常处理
-
1.try-except
""" try: 有可能出现异常的代码 except 异常类型 as 变量 处理后的代码 """ def test(): try: print(a) except Exception as e: print("Exception: {}".format(e)) test() # Exception: name 'a' is not defined
-
2.try-except-except(捕获多个异常,如果前面就捕获到了,后面的except就不执行,类似于if else)
def test(): try: a = 1 / 0 print(a) except NameError as e: # 第一种写法,用as e把错误赋值给e print("NameError:{}".format(e)) except ZeroDivisionError: # 第二种写法,不写as 自定义输出报错内容 print("ZeroDivisionError: 除数不能为0") test() # ZeroDivisionError: 除数不能为0
-
3.try-except-else
else 与 return 的执行顺序,有return时else是不执行的.无return时, 正常执行else如果发生异常则不执行else
如果没有异常,则执行else里面的代码,例如:
def test(): try: a = 10 except (NameError, ZeroDivisionError) as e: # 用元组把可能报错的异常类型囊括进去,避免写多行except print("Error:{}".format(e)) else: print("No Error :{}".format(a)) test() # No Error :10
-
4.try-except-finally
不管代码是否有异常,最后都会执行finally里面的代码。
finally 与 return 的执行顺序,即使有return也要执行finally的, 而且是在return 之前执行它
def test(): try: a = 10 1 / 0 except (NameError, ZeroDivisionError) as e: print("{}, {}".format(type(e), e)) finally: print("Process over!!") test() # <class 'ZeroDivisionError'>, division by zero # Process over!!
-
自定义异常
自定义异常可用于引发一个异常(抛出一个异常),由关键字raise引发。
class MyError(Exception): # 自定义异常,继承Exception类 def __init__(self, length, min_len): self.length = length self.min_len = min_len def __str__(self): return "你输入的长度是%s,不能少于%s" % (self.length, self.min_len) def main(): try: con = input("请输入密码:") l = len(con) if l < 6: raise MyError(l, 6) # raise抛出异常,raise+异常类型(message) except Exception as e: print(e) else: print("您的密码输入完毕") main() # 请输入密码:2 # 你输入的长度是1,不能少于6
class MyError(Exception): # 自定义异常,继承Exception类 def __init__(self, length, min_len): self.length = length self.min_len = min_len def __str__(self): return "你输入的长度是%s,不能少于%s" % (self.length, self.min_len) def test(): con = input("请输入密码:") l = len(con) if l < 6: raise MyError(l, 6) test() """请输入密码:123 Traceback (most recent call last): File "/Users/Yang/Documents/Python-Study/全能工程师/01-python基础/exam.py", line 43, in <module> test() File "/Users/Yang/Documents/Python-Study/全能工程师/01-python基础/exam.py", line 27, in test raise MyError(l, 6) __main__.MyError: 你输入的长度是3,不能少于6"""
-
assert(断言)
assert断言函数是对表达式布尔值的判断,要求表达式计算值必须为真,如果表达式为假,触发异常;如果表达式为真,不执行任何操作
函数原型:
assert expression
assert expression 等价于下面的个句式: if __debug__: if not expression: raise AssertionError assert也可以用于多个表达式的断言: assert expression1, expression2
应用场景
通常情况传递参数不会有误,但编写大量的参数检查影响编程效率,而且不需要检查参数的合法性。
排除非预期的结果。
# 1.判断 assert 1==2, '1 不等于 2' # 如果1==2正常,否则抛出异常1 不等于 2 # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # AssertionError: 1 不等于 2 # 2.函数的参数检查 def check_user_info(self, **kwargs): assert len(kwargs) == 4, '参数必须是4个' # 参数必须是4个,否则异常 # 3.只在Linux下运行 import sys assert ('linux' in sys.platform), "该代码只能在 Linux 下执行" # 如果系统是Linux正常运行,否则异常
模块与文件
1.模块和包
-
模块(Module)和包(Package)
# 模块:一个包含所有你定义的函数和变量的文件,其后缀名是 .py ,一个.py文件就是一个模块 # 包:一定包含 __init__.py模块 的文件夹,一般也会包含其他一些模块和子包 # 库(lib):库是完成一定功能的代码集合,具体表现可以是包,也可以是一个模块 # 框架(framework):为解决一个开放性问题而设计的具有一定约束性的支撑结构 # python内置了一些库,除此之外,还有其他人自己做的一些库,称之为第三方库 # 包和模块导入都是用import import package # 只会拿到对应包下的__init__中的功能或者当前模块下的功能 # 从包中导入特定模块 form package import module # package:包的来源. module:目标模块 form package import module as test # 将导入的模块名改为test # time模块 import time print(time.ctime()) # Tue Nov 16 12:40:44 2021
-
os模块
import os os.getcwd() # 获取当前工作目录 os.chdir("../test") # 改变当前脚本工作目录,相当于shell下的cd命令 os.rename("毕业论文.txt","毕业论文最终版.txt") # 重新命名文件 os.remove("毕业论文.txt") # 删除文件 os.rmdir("demo") # 删除空文件夹 os.removedirs("demo") # 删除空文件夹 os.mkdir("demo") # 创建一个文件夹 os.listdir("/Documents") # 列出文件夹目录里所有的文件和文件夹 os.chdir("/Documents") # 切换到工作目录 os.name # nt -> windows posix -> Linux/Unix或者MacOs os.environ # 获取到环境配置 os.environ.get("PATH") # 获取到指令路径的环境配置 os.path.abspath(path=) # 获取path规范会的绝对路径 os.path.exists(path=) # 如果path存在,返回True os.path.isdir(path=) # 如果path是文件夹,返回True os.path.isfile(path=) # 如果path是文件,返回True os.path.splitext(path=) # 用来将指定路径进行分割,可以获取到文件的后缀名
-
sys模块
import sys sys.path() # 将你的模块放在这些路径里面,就能用import导入 """ ['/opt/homebrew/bin', '/opt/homebrew/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python39.zip', '/opt/homebrew/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9', '/opt/homebrew/Cellar/python@3.9/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/lib-dynload', '', '/opt/homebrew/lib/python3.9/site-packages', '/opt/homebrew/lib/python3.9/site-packages/IPython/extensions', '/Users/Yang/.ipython'] """ sys.argv # 传递给Python脚本的命令行参数列表 sys.exit(code) # 让程序以指定的退出码结束 sys.stdin # 可以像input一样,接收用户的输入。 sys.stdout # 标准输出。可以通过修改它来改变默认输出 sys.stderr # 错误输出。可以通过修改它来改变错误输出
-
Pexpect模块
Pexpect 是一个用来启动子程序并对其进行自动控制的纯 Python 模块。 Pexpect 可以用来和像 ssh、ftp、passwd、telnet 等命令行程序进行自动交互。继第一部分《探索 Pexpect,第 1 部分:剖析 Pexpect 》介绍了 Pexpect 的基础和如何使用后,本文将结合具体实例入手,详细介绍 Pexpect 的用法和在实际应用中的注意点。
详见:https://www.cnblogs.com/mmdln/p/9006274.html
2.文件操作
-
文件创建和写入
详见:https://cloud.tencent.com/developer/article/1570492
3.常用函数与高阶函数
-
加密模块hashlib与base64
详见:https://www.cnblogs.com/xiao-apple36/p/8744213.html
-
logging模块
logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:
- 可以通过设置不同的日志等级,在release版本中只输出重要信息,而不必显示大量的调试信息;
- print将所有信息都输出到标准输出中,严重影响开发者从标准输出中查看其它数据;logging则可以由开发者决定将信息输出到什么地方,以及怎么输出。
详见:https://www.cnblogs.com/Nicholas0707/p/9021672.html
第一种使用方式:简单配置
import logging LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s " # 配置输出日志格式 DATE_FORMAT = "%Y-%m-%d %H:%M:%S %a " # 配置输出时间的格式,注意月份和天数不要搞乱了 # logging.basicConfig()函数可以调整日志级别、输出格式等 logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=DATE_FORMAT, filename=r"./test.log" # 有了filename参数就直接写入文件而不是终端 ) logging.debug("msg1") logging.info("msg2") logging.warning("msg3") logging.error("msg4") logging.critical("msg5") """ logging.basicConfig()函数是一个一次性的简单配置工具使,也就是说只有在第一次调用该函数时会起作用,后续再次调用该函数时完全不会产生任何操作的,多次调用的设置并不是累加操作。"""
第二种使用方式:日志流处理流程
import logging def log(): #创建logger,如果参数为空则返回root logger logger = logging.getLogger("nick") logger.setLevel(logging.DEBUG) #设置logger日志等级 #这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志 if not logger.handlers: #创建handler fh = logging.FileHandler("test.log",encoding="utf-8") ch = logging.StreamHandler() #设置输出日志格式 formatter = logging.Formatter( fmt="%(asctime)s %(name)s %(filename)s %(message)s", datefmt="%Y/%m/%d %X" ) #为handler指定输出格式 fh.setFormatter(formatter) ch.setFormatter(formatter) #为logger添加的日志处理器 logger.addHandler(fh) logger.addHandler(ch) return logger #直接返回logger logger = log() logger.warning("泰拳警告") logger.info("提示") logger.error("错误") logger.debug("查错")
-
迭代器与生成器
Python标准库中存在着一些可迭代对象,例如:list, tuple, dict, set, str等。
可以对这些迭代对象,进行for-in等迭代操作,例如:
for s in "helloworld": print(s)
编译器若想迭代一个对象a,则会自动调用iter(a)获取该对象的迭代器(iterator),如果iter(a)抛出异常,则对象a不可迭代。
总结迭代器的优缺点:
优点:
1,提供了一种不依赖索引的迭代取值方式
2,节省内存
缺点:
1,只能往后取,不能往前取,而且是一次性的,值取干净之后无法再取值,除非重新得到新的迭代器对象
不如按索引取值的方式灵活
2,值取不干净,永远无法预测迭代器的长度 -
递归函数
def Recursion(n): # 判断参数n是否为1或0,如果是返回1 if n == 0 or n == 1: return 1 # 否则计算并调用本身进行递归,return返回计算结果 else: return n * Recursion(n - 1) print(Recursion(5)) #120
-
匿名函数lambda函数
1.lambda的格式:lambda 参数:表达式
# 普通函数和lambda的对比 def sum_func(a, b, c): return a + b + c sum_lambda = lambda a, b, c: a + b + c print(sum_func(1, 100, 10000)) #10101 print(sum_lambda(1, 100, 10000)) #10101 # 无参数 lambda_a = lambda: 100 print(lambda_a()) #100 # 一个参数 lambda_b = lambda num: num * 10 print(lambda_b(5)) #50 # 多个参数 lambda_c = lambda a, b, c, d: a + b + c + d print(lambda_c(1, 2, 3, 4)) #10 # 表达式分支 lambda_d = lambda x: x if x % 2 == 0 else x + 1 print(lambda_d(6)) #6 print(lambda_d(7)) #8
2.把lambda当作参数
def sub_func(a, b, func): print('a =', a) #a = 100 print('b =', b) #b = 1 print('a - b =', func(a, b)) #a - b = 99 sub_func(100, 1, lambda a, b: a - b)
3.lambda函数与Python内置函数配合使用
member_list = [ {"name": "风清扬", "age": 99, "power": 10000}, {"name": "无崖子", "age": 89, "power": 9000}, {"name": "王重阳", "age": 120, "power": 8000} ] new_list = sorted(member_list, key=lambda dict_: dict_["power"]) print(new_list) # [{'name': '王重阳', 'age': 120, 'power': 8000}, {'name': '无崖子', 'age': 89, 'power': 9000}, {'name': '风清扬', 'age': 99, 'power': 10000}] number_list = [100, 77, 69, 31, 44, 56] num_sum = list(map(lambda x: {str(x): x}, number_list)) print(num_sum) # [{'100': 100}, {'77': 77}, {'69': 69}, {'31': 31}, {'44': 44}, {'56': 56}]
-
map()函数
map() 会根据提供的函数对指定序列做映射。
map(function, iterable, …)
function – 函数,有两个参数
iterable – 一个或多个序列
它有两个参数,第一个参数为某个函数,第二个为可迭代对象。# example:将列表的每个元素平方,组成新的列表 # 常规方法 li1 = [1,3,4,5,7,10] def pow_test(n): return n ** 2 def map_test(fun,array): li = [] for i in array: res = fun(i) li.append(res) return li res1 = map_test(pow_test,li1) print(res1) # 这里的map_test函数的参数fun传入的是一个函数,即这个map_test函数是高阶函数,在map_test函数里调用其他函数,对第二个传入的对象进行处理。两个这里map_test()、pow_test()加在一起实现的功能就是map()函数的作用。 # 用map()函数处理 li1 = [1,3,4,5,7,10] # 下面两个方法都可以,一个是自定义的pow_test函数,一个是匿名函数lambda(高级用法) # res1 = list(map(lambda x:x**2,li1)) # res1 = list(map(pow_test, li1)) print(res1) # map()函数的第一个参数是一个函数,即匿名函数,这里不需要加括号执行,只是代表对一个参数的处理方式,map()第二个参数是待处理的可迭代对象。map()函数直接处理结果是一个map()函数的内存地址,是一个可迭代类型,需要加list转换为列表。这里map()函数的第一个参数不一定非要写lambda函数,也可以写自定义的函数名。
-
filter()函数
filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。filter:过滤,滤除
# example: 将列表[1, 4, 6, 7, 9, 12, 17]中删除偶数,保留奇数 li = [1, 4, 6, 7, 9, 12, 17] def filter_old(n): if n % 2 == 1: return n res = list(filter(filter_old,li)) print(res) # 也可以 li = [1, 4, 6, 7, 9, 12, 17] res = list(filter(lambda n:n %2 ==1,li)) print(res) # 这里的lambda函数是一个函数处理逻辑,最终是返回了符合 n %2 ==1条件的n,这里与map函数一样,也需要List将其转换为列表。 # lambda的其他用法 li = [ {"name":"jack","age":53}, {"name":"pony","age":46}, {"name":"charles","age":53}, {"name":"richard","age":44} ] v = list(filter(lambda p:p["age"]<45,li)) # lambda也可以做判断 print(v) # 这里lambda返回的就是符合条件的元素p,遍历序列中的每个元素,判断每个元素得到布尔值,如果是True则留下来。
-
reduce()函数
# example,返回列表li = [1,2,3,4,5,6]每个元素相乘最终结果 # 需要从functools里面import,python3自带的没有reduce from functools import reduce li = [1,2,3,4,5,6] v = reduce(lambda x,y:x*y,li) print(v)
-
map()、filter()、reduce()小结
map(做处理):处理序列中的每个元素,得到的结果是一个‘列表’(内存地址),该‘列表’元素个数及位置与原来一样
filter(做判断):遍历序列中的每个元素,判断每个元素得到布尔值,如果是True则留下来
reduce():处理一个序列,然后把序列进行合并操作
# lambda的处理不同,返回也不同 test_list = [1, 2, 3, 4, 5, 6] print(list(map(lambda x: x ** 2, test_list))) # 对列表每个元素做处理,返回处理结果 print(list(map(lambda n: n % 2 == 1, test_list))) # 对列表每个元素做判断,返回判断结果 print(list(filter(lambda n: n % 2 == 1, test_list))) # filter对判断结果为True的存入列表 print(reduce(lambda x, y: x * y, test_list)) # 对整个列表做合并处理 # [1, 4, 9, 16, 25, 36] # [True, False, True, False, True, False] # [1, 3, 5] # 720