列表推导式:[out_express for out_express in input_list]
例:生成一个[0,1,4,9,16]的列表。
lis = []for i in range(5): mul_i = i*i lis.append(mul_i)print(lis)
odd_list = [i*i for i in range(5)]print(odd_list)
当列表推导式还需要满足某条件下才输出表达式时,可以通过以下语法规范实现:
[out_express for out_express in input_list if out_express_condition]
例:
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, -4]
将li列表中小于0的元素平方并且保存到新列表中。
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, -4]new_li = [i**2 for i in li if i<0]print(new_li)
嵌套循环推导式:[i for row in matrix for i in row]
例:生成列表li为['1a', '1b', '1c', '2a', '2b', '2c', '3a', '3b', '3c']
li = [i+j for i in "123" for j in "abc"]
字典推导式:{out_exp_key: out_exp_value for out_exp in input_list}
例子:生成字典;字典的key值为列表li的元素索引;字典的value值为列表的元素;li = ["age","name","gender"]
li = ["age","name","gender"]dic = {li.index(i):i for i in li}print(dic)
并且字典推导式可以快速将字典中的k,v互换
dic1 = {0:"0",1:"1",2:"2"}dic2 = {v:k for k,v in dic1.items()}print(dic2)
集合推导式:{out_exp_res for out_exp in input_set} 例:随机生成10个1-100之间的元素,并且去重
s1 = {random.randint(1,100) for i in range(10)}print(s1)print(len(s1)
二、迭代器迭代器指的是迭代取值的工具,迭代是指一个重复的过程,每一次重复都是基于上一次结果而来
迭代提供了一种通用的不依赖索引的迭代取值方式。
可迭代对象:可以用for循环遍历的对象都是可迭代对象。
str,list,tuple,dict,set等都是可迭代对象。generator,包括生成器和带yield的生成器函数。
判断是否可迭代:
除了看内置是否含有__iter__方法来判断该对象是否是一个可迭代的对象之外,我们还可以使用 isinstance() 判断一个对象是否是 Iterable 对象
isinstance()-->用来判断对象是否是相应类型,与type()类似。
from collections import Iterable,Iteratorprint(isinstance('abc',Iterable)) # Trueprint(isinstance([1,2,3,4],Iterable)) # Trueprint(isinstance(123,Iterable)) # False
迭代器对象
●有内置的__next__()方法的对象,执行该方法可以不依赖索引取值
●有内置的__iter__()方法的对象,执行迭代器的__iter__()方法得到的依然是迭代器本身
需要注意的是,可迭代对象不一定是迭代器。
from collections import Iterable,Iteratorli = [1,2,3,4]print(isinstance(li,Iterator)) # False
iter()
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
那我们可以通过iter()方法将可迭代的对象,转为迭代器。
li = [1,2,3,4]lis = iter(li)print(type(lis)) #
notice:
三、生成器迭代器不可以通过下标取值,而是使用__next__()或者next()。但是只要超出范围则直接报错StopIteration。
print(lis[0]) # 报错 not subscriptableprint(lis.__next__())print(lis.__next__())print(lis.__next__())print(lis.__next__())print(next(lis))print(next(lis))print(next(lis))print(next(lis))
next()只能顺延调用,不能往前。可迭代对象与迭代器区别
可用于for循环的都是可迭代类型作用于next()都是迭代器类型list、dict、str等都是可迭代的但不是迭代器,因为next()函数无法调用它们。可以通过iter()函数将它们转为迭代器python的for循环本质就是通过不断调用next()函数实现的生成器定义:在Python中,一边循环一边计算的机制,称为生成器:generator。
优点:在循环的过程中根据算法不断推算出后续的元素,这样就不用创建整个完整的列表,从而节省大量的空间。
生成器表达式:来源于迭代和列表解析的组合,生成器和列表解析类似,但是它使用()而不是[]。
g = (x for x in range(5))print(g) # generator objectprint(next(g))print(next(g))print(next(g))print(next(g))print(next(g))# 超出报错print(next(g))for i in g: print(i)
当一个函数中包含yield关键字,那么这个函数就不再是一个普通的函数,而是一个generator。调用函数就是创建了一个生成器对象。其工作原理就是通过重复调用next()或者__next__()方法,直到捕获一个异常。
def yieldtest(number): n = 0 # li = [] while n
notice:yield返回一个值,并且记住这个返回值的位置,下次遇到next()调用时,代码从yield的下一条语句开始执行。与return的差别是,return也是返回一个值,但是直接结束函数。
例:实现斐波那契数列,除第一个和第二个数外,任何一个数都可以由前两个相加得到:
1,1,2,3,5,8,12,21,34.....
def createNums(): print("-----func start-----") a,b = 0,1 for i in range(5): # print(b) print("--1--") yield b print("--2--") a,b = b,a+b print("--3--") print("-----func end-----") g = createNums()print(next(g)) print(next(g)) print(next(g))print(next(g))print(next(g))
send() 和next()一样,都能让生成器继续往下走一步(遇到yield返回),但send()能传一个值,这个值作为yield表达式整体的结果。
def test(): a1 = yield "hello" print("---1---") yield a1res = test()print(next(res)) # "hello"print(res.send("world")) # "world"
也就是说,通过send方法可以强行修改上一个yield表达式值。
比如函数中有一个yield赋值,a1 = yield "hello",第一次迭代到这里会返回"hello",但是a1还没进行赋值。第二次迭代时,使用.send("world"),那么,就是相当于强行修改yield "hello"表达式的值为"world",所以yield a1结果为"world"。
迭代器与生成器:
生成器能做到迭代器能做的所有事而且因为生成器自动创建了iter()和next()方法,生成器显得简洁,而且高效。
四、面向对象
面向对象编程:Object Oriented Programming,简称OOP,是一种程序设计思想。
面向过程:根据业务逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装。减少重复代码的重写过程
面向对象概念及术语
类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称作类的实例。 对象:也称实例。通过类定义的初始化方法,赋予具体的值,成为一个"有血有肉的实体"。
实例化:创建类的实例的过程或操作。 实例变量:定义在实例中的变量,只作用于当前实例。
类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。 数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
方法:类中定义的函数。 静态方法:不需要实例化就可以由类执行的方法
类方法:类方法是将类本身作为对象进行操作的方法。方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override。
封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制继承:即一个派生类(derived class)继承父类(base class)的变量和方法。多态:根据对象类型的不同以不同的方式进行处理。
类与对象介绍
类:是抽象的概念,仅仅是模板。用来描述具有相同属性和方法的对象的集合。比如:"人"是一个类。
对象:某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。 比如:"胡歌"就是一个对象。
类与对象的关系:那么实际上,我们可以进行对象归类。
类由3个部分构成 :
类的名称:类名类的属性:一组数据
类的方法:允许对类进行操作的方法
notice:类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。
类的定义:Python使用class关键字来定义类,其基本结构如下:
class 类名:
pass
创建对象
python中,可以根据已经定义的类去创建出一个个对象
创建对象的格式为 : 对象名 = 类名()
创建类:学生类创建对象:张三在类中定义方法输出:张三学习Python
class LogicStudents:
def study_python(self):
print("张三学习python")zs = LogicStudents()
zs.study_python()
总结:
# 定义
class 类名:
def 方法名(self,参数): # 类中函数:称为方法
pass
# 执行
s = 类名() # 创建对象(实例) 整个过程就是实例化
s.方法名(参数) # 调用类中方法
self参数
在类当中定义方法时,会发现系统帮我们自动创建了self参数,并且在调用对象的该方法时,也无需传入self参数。那这个self是什么?
实际上,我们需要明确self的两个概念
self本身是形参self就是对象本身
例:定义类为:学生类
●创建对象:李四
●在类中定义方法:打印李四信息_init__()方法
__init__()方法称为初始化方法,也可称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
def info(self):
print(self.name,self.age)ls = Student("李四",18)
ls.info()
_str__()方法
如果在开发中,希望打印输出对象变量时,能够打印自定义的内容。就可以使用__str__()方法,将自定义内容通过return关键字返回。
class Students:
def __init__(self):
self.name = "李四"
self.age = 18def __str__(self):
return self.name
ls = Students()
print(ls) # 李四
私有属性:就是对象不希望公开的属性
定义方式:在属性名前面增加两个下划线(例如:__name)
例:
定义类为:"人"类
●创建对象:rose
●初始化对象属性:name与age
●要求:age不能在类的外部访问
class Person: def __init__(self): self.name = "肉丝" self.__age = 20 # 私有属性定义rose = Person()print(rose.name) # "肉丝"print(rose.__age) # AttributeError: 'Person' object has no attribute '__age'
私有方法:就是对象不希望公开的方法
定义方式:在方法名前面增加两个下划线(例如:__test)
class Demo: def test1(self): print("--1--") def __test2(self): # 私有方法 print("--2--") def test3(self): print("--3--")d = Demo()d.test1()d.__test2() # AttributeError: 'Demo' object has no attribute '__test2'd.test3()
五、成员
成员介绍:类的成员可分为字段、方法以及属性。
字段又主要可以分为普通(实例)字段与静态(类)字段。
普通字段(实例属性)
普通字段属于对象,保存在对象中,只能通过对象访问。
定义:self.字段名称
访问:self.字段名称 或 对象.字段名称
静态字段(类属性)
静态字段属于类,保存在类中。在创建时,仅创建一份,并且所有对象都共享静态字段。执行时可以是类访问也可以是对象访问。
定义:直接在类中定义
访问:类名.字段名称 或 self.字段名称 或 对象.字段名称
方法介绍
类的方法主要可分为普通方法(实例方法),类方法,静态方法。
普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必须为系统自建参数,默认为self,代指对象本身。
notice:self仅仅是变量名。使用其它也可,但最好不要修改
静态方法通过在方法上面添加@staticmethod装饰器定义,保存在类中;
静态方法不需要传入self参数,即使传入了self参数也并不像普通方法那样代指对象本身,仅仅是一个普通的形参。
静态方法的调用虽然可以通过对象调用,但一般由类调用。
类方法通过在方法上面添加@classmethod装饰器,保存在类中;
类方法不需要传入self。但有一个系统自建参数为cls,cls代指类本身;
类方法一般通过类调用,也可通过对象调用。
一般而言
访问字段:对象.字段名称访问方法:对象.方法名称([参数])
属性
在实际开发中,为了简便,当想访问方法以属性的访问形式时,就可以通过在方法上面添加@property装饰器,达到该效果。
定义及访问如下:
class Demo: def __init__(self): self.name = "rose" @property def test(self): print(self.name)d = Demo()d.test # 通过@property 以 普通属性 形式 访问 普通方法
notice:以上@property只相当于一个只读属性,也就是说,仅能访问test函数的内容。
需求:
1.通过传参去改变self.name的值
2.删除掉self.name
class Demo: def __init__(self): self.name = "rose" @property def test(self): # 只可访问模式 print(self.name) @test.setter # d.set_test = "jack" def test(self,para): self.name = para print(self.name) @test.deleter def test(self): # del d.test del self.name print(self.name) # 报错 self.name已被删除d = Demo()d.test # 通过@property 只读模式# 改变self.name的值d.test = "jack" # 给方法传参 以 赋值方式 会触发@test.setter# 删除self.namedel d.test # del 会 触发 @test.deleter
例:实现分页
思路
用户输入查看的页面比如输入1-->[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
比如输入2-->[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
面向过程代码:
li = [i for i in range(1000)] # 比如一共100页while True: p = int(input("请输入要查看的页码:")) start = (p - 1) * 10 end = p * 10 print(li[start:end])
需求
使用面向对象编程思想实现并且在类中定义start与end方法
通过调用start与end方法实现(尽量通过调用属性的方式实现)
六、封装继承多态
面向对象的三大特性是指:封装,继承与多态。
封装介绍:封装是面向对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或者方法,隐藏功能的实现细节,也可以设置访问权限。
class Student: def __init__(self,name,age): self.name = name # 字段封装到了类的内部 self.age = age def prin_info(self): # print(ls.name,ls.age) print(self.name,self.age)ls = Student("李四",18)# ls.name = "李四" # 字段(属性)定义在类的外部# ls.age = 18ls.prin_info()
继承介绍:继承是一种创建新类的方式,如果子类需要复用父类的属性或者方法时,就可以使用继承。当然,子类也可以提供自己的属性和方法。
class Father: # 父类 超类 pass class Son(Father): # 子类 派生类 pass
例:验证Python3中,全部都是新式类
实现思路:
比较继承与无继承两个空类的成员是否一致
拓展方法:
对象.__dir__() 查看对象的属性与方法
class Father(object): passclass Son: passf = Father()s = Son()print(len(f.__dir__())) # 26print(len(s.__dir__())) # 26
单继承:子类继承父类,则可以直接享受父类中已经封装好的方法
super():super()函数是用于调用父类(超类)的一个方法。
语法:super(type[, object-or-type])
type-->类object-or-type -- 类,一般是 self
多继承:所谓多继承,即子类有多个父类,并且具有它们的特征。
多态:是应用于Java和C#这一类强类型语言中,而Python崇尚"鸭子类型"
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。
七、常用魔法方法
魔法方法介绍
在Python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动被调用,称为魔法方法。
魔法方法的命名总是被双下划线包围,比如__名称__。
__doc__
__doc__用来查看类的说明文档
例:查看列表类的说明文档
print(list().__doc__)
__module__用来查看当前操作的类所在模块
当执行模块是类所在模块时,执行结果为__main__。否则,执行结果是类所在模块的名称。
__class__
__class__用来查看当前对象的类
类也是对象,Demo是type类的对象,d是Demo类的对象。
__dict__
__dict__用于获取类或者实例的属性字典
notice:
普通字段存储在对象中,所以通过对象.__dict__获取的是普通字段除普通字段以外的成员都存储在类中,所以通过类.__dict__来获取
__del__()方法
__del__()方法也叫做析构方法。当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。
notice:
当代码全部执行完毕才自动触发__del__()如果需要提前触发,则需要通过del关键字,删除所有对象后触发__del__()
此方法一般不需要定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配与释放,一般都是交给Python解释器来执行。所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行。
__call__()方法
__call__()方法用于将对象变成一个可调用的对象。也就是说,当一个类中有__call__()方法时,其实例化得到的对象便是可调用的(callable)
class Demo(object): passd = Demo()d() # TypeError: 'Demo' object is not callable
_new__()方法
__new__()方法用于创建与返回一个对象。在类准备将自身实例化时调用。
class Demo(object): def __init__(self): print("__init__") def __new__(cls, *args, **kwargs): print("__new__")d = Demo()
notice:
__new__()方法用于创建对象__init__()方法在对象创建的时候,自动调用
但是此处重写了父类的__new__()方法,覆盖了父类__new__()创建对象的功能,所以对象并没有创建成功。所以仅执行__new__()方法内部代码
对象创建执行顺序
1.通过__new__()方法创建对象2.并将对象返回,传给__init__()
例:在自定义类中实现创建对象
思路
●重写父类__new__()方法
●并且在该方法内部,调用父类的__new__()方法
class Demo(object): def __init__(self): print("__init__") def __new__(cls, *args, **kwargs): print("__new__") return super().__new__(cls)d = Demo()
notice:
在创建对象时,一定要将对象返回,在会自动触发__init__()方法__init__()方法当中的self,实际上就是__new__返回的实例,也就是该对象
__init__()与__new__()区别
__init__实例方法,__new__静态方法__init__在对象创建后自动调用,__new__创建对象的方法
单例模式是一种常用的软件设计模式。也就是说该类只包含一个实例。
通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
例:单例模式实现
思路
当对象不存在时,创建对象当对象存在时,永远返回当前已经创建对象
反射
在实际开发时,当开发者想要执行对象里的方法或者属性时,但是无法确定该方法及属性一定存在。这时就需要使用一些特殊的方法来访问与操作这个未知的方法或属性,那么此时程序员即会使用反射处理。
反射实际上就是通过字符串的形式,导入模块;通过字符串的形式,去模块寻找指定函数,并执行。利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,是一种基于字符串的事件驱动。