python进阶语法

Python进阶语法

1.函数式编程

函数也是一种数据,数据类型是function,函数式编程是基于函数进行的一系列操作。

1.1函数作为参数

即回调函数(callback),在调用函数时使用函数作为形参和实参

  • “工具函数”:只是找到对应函数的工具,不负责内部处理,而是由业务函数来处理

  • “业务函数”:也是传入的函数,即回调函数,用于对数据做处理,作为参数被别的函数调用

    可以类比前面学过的map(),filter(),reduce()等函数,它们的第一个参数都是函数,负责处理的是匿名函数(也可以单独调用函数来使用),第二个参数是传入的待处理的数据

#工具函数
def find_odd(num,tool):
    for el in num:
        if el % 2 == 1:
            # print("找到其中一个奇数:",el)
            tool(el) #调用函数,在这里是一个形式函数(形参)
l1=[1,2,3,4,5,6,7,8,9,10]
#业务函数1
def print_odd(el):
    print("找到其中一个奇数:",el)
#业务函数2
def print_odd2(el):
    print("处理后的奇数:",el**2)
find_odd(l1,print_odd)
find_odd(l1,print_odd2)

#自己定义的map(),fliter(),reduce()函数:
def fun_filter(tool,l):
    l2=[]
    for el in l:
          if tool(el):
            l2.append(el)    
    return l2
def fun_map(tool,l):
    l3=[]
    for el in l:
          re=tool(el)
          l3.append(re)
    return l3
def fun_reduce(tool,l,first_num):
    if len(l)==0:
        first_num=l[0]
        start=1
    else:
         start=0
    for el in l[start:]:
          first_num=tool(first_num,el)
    return first_num
##处理map的业务函数,x是传入的值 
def fun1(x):
	return x
#处理filter的业务函数,x是传入的值   
def fun2(x):
    return x%2==0
#处理reduce的业务函数,y是传入的值,x是初始的值,也是下一次递归调用时的初始值        
def fun3(x,y):
    return x+y 
l1=[1,2,3,4,5]
print(fun_map(fun1,l1))
print(fun_filter(fun2,l1))
print(fun_reduce(fun3,l1,10))

#自己的示例,写一个程序,一个平台是查询钱包余额,另一个平台得到钱包金额的数据后自动抢对应价位的live票
#通过钱包余额记录票的种类,属于工具函数
def ticket_buy(fun_c,d):
    cash=fun_c(d)
    if cash>=888:
        return "ss特等席"
    elif cash>=588 and cash<888:
        return "s特等席"
    elif cash>=288 and cash<588:
        return "A特等席"
    else:
        return "无席位"     
 
#记录金额总数,是回调函数
def wallet_check(d):
    return d['百元']*100+d['五十元']*50+d['十元']*10+d['一元']*1
   
cash_dict_str=(input("请输入钱包对应的金额和数量('百元','五十元','十元','一元'),以空格隔开:"))
cash_dict_list=cash_dict_str.split()
cash_dict={cash_dict_list[i]:int(cash_dict_list[i+1]) for i in range(0,len(cash_dict_list),2)}
print(ticket_buy(wallet_check,cash_dict))

1.2函数作为返回值

即函数的闭包(block),初步理解,函数的内部还有函数,内部的那个函数就是闭包

闭包必须满足以下三个条件:

  • 必须有一个内嵌函数
  • 内嵌函数必须引用外层函数中变量
  • 外部函数返回值必须是内嵌函数

下面通过几个方面示例:

  • 由于函数的功能不同,所以函数之间的变量互不干扰,需要分开作用域
def 所有函数():
	def fun1():
		内容
		
	def fun2():
		内容
  • 函数内部的变量,在函数外部不能被直接访问,但是可以间接访问
  • 由于函数的定义和调用的作用域不同,并且函数的调用是在函数被定义的作用域执行的
  • 通过返回这个闭包函数,就可以实现在外部访问函数内部的变量
#用一个app之间的授权交流的例子来理解
def sakanaction_app():
    point_all=3000
    def point_refresh(type):
		#访问外部变量并修改
        nonlocal point_all
        if type=='video':
            point_all+=100
        elif type=='text':
            point_all+=50
        elif type=='message':
            point_all+=20
        else:
            point_all+=1
        print(point_all)
    return point_refresh
#用于获取用户行为的app,user_type是在tixplus_app内用于调用函数而定义的
def tixplus_app():
    user_type=sakanaction_app()
    user_type('video')
    user_type('text')
    user_type('message')
    user_type('none')    
tixplus_app()  

#购物的例子
def del_goods(money):
	#闭包的函数
    def buy_goods(goods,price):
        nonlocal money 
        if money>=price:
            money-=price
            print(f"购买了{goods},剩余余额为{money}")
    
        else:
            return f'余额不足,无法购买{goods}'
	#返回闭包函数
    return buy_goods
#用一个变量接受函数的返回值,其类型也是函数
good=del_goods(2000)
good('毛巾',1800)
good('勋章',1500)
good('立牌',1000)
  • 闭包不一定是单独定义的函数,只要有单独的作用域就行(如后面学的装饰器,类,对象,数据容器(内部有函数)就是闭包)

–重要:一个例子理解回调和闭包–

def fun(num=None,n=None):
	print(n)
	return {"fun": lambda x:fun(x,num)}
#情形1
type_a=fun(0)
type_a["fun"](1)
type_a["fun"](2)
type_a["fun"](3)
#情形2
type_b=fun(0)["fun"](1)["fun"](2)["fun"](3)
#情形3
type_c=fun(0)["fun"](1)
type_c["fun"](2)
type_c["fun"](3)

#流程分析伪代码,使用{}来代表一个作用域
'''
全局作用域global{
	#情形1
    语句type_a=fun(0)调用fun()函数,传入参数0
    【标识符fun】
	 A1>>>fun(0)->{
        标识符num=0
		标识符n=None
        print(n)
		这里num=0,故传入
		return {"fun": lambda x:fun(x,0)}
	
	 A1>>>type_a["fun"](1)这里是在对函数a的返回的字典取键值为"fun"调用fun()函数,传入参数1,所以调用时还是在A1作用域内
		{
			标识符num=1
			在本作用域未找到n的值,在外层作用域用寻找
			return {"fun": lambda x:fun(1,0)}传入数据num的值为1
		}

		A1>>>type_a["fun"](2)这里是在对函数a的返回的字典取键值为"fun"调用fun()函数,传入参数2,所以调用时还是在A1作用域内
		{
			标识符num=2
			在本作用域未找到n的值,在外层作用域用寻找
			return {"fun": lambda x:fun(2,0)}传入数据num的值为2
		}

		A1>>>type_a["fun"](3)这里是在对函数a的返回的字典取键值为"fun"调用fun()函数,传入参数3,所以调用时还是在A1作用域内
		{
			标识符num=3
			在本作用域未找到n的值,在外层作用域用寻找
			return {"fun": lambda x:fun(3,0)}传入数据num的值为3,所以返回fun(3,0)
		}

	}



A2>>>type_a["fun"](1)这里是在返回的字典中调用fun()函数,所以调用时要新生成一个作用域A2
	{
		标识符num=1
		标识符n=0
		print(n)输出0
		return {"fun": lambda x:fun(x,num)}
	}

A2>>>type_a["fun"](2)同理
	{
		标识符num=1
		标识符n=0
		print(n)输出0
		return {"fun": lambda x:fun(x,num)}
	}

A2>>>type_a["fun"](3)同理
	{
		标识符num=3
		标识符n=0
		print(n)输出0
		return {"fun": lambda x:fun(x,num)}
	}

#情形2
type_b=fun(0)["fun"](1)["fun"](2)["fun"](3)首先会执行第一层type_b=fun(0),传入num=0
B1>>>type_b=fun(0)
{
	标识符num=0
	标识符n=None
	print(n)输出None
	return {"fun": lambda x:fun(x,num)} 

	B1>>>type_fun(0)["fun"](1)传入参数为1,此时是在该返回的字典中调用fun()函数,所以执行该段代码仍是在B1作用域内
	{
		标识符num=1
		在本作用域未找到n的值,在外层作用域用寻找n=0,并作为参数传给fun()函数
		return {"fun": lambda x:fun(1,0)}
	}

}


B2>>>type_fun(0)["fun"](1)这里对返回的字典调用fun()函数,所以要在新生成一个作用域B2
	{
		标识符num=1
		标识符n=0
		print(n)输出0
		return {"fun": lambda x:fun(x,num)}

		B2>>>type_fun(0)["fun"](1)["fun"](2)
		{
			标识符num=2
			在本作用域未找到n的值,在外层作用域用寻找n=1,并作为参数传给fun()函数
			return {"fun": lambda x:fun(2,1)}
		}
	}

B3>>>type_fun(0)["fun"](1)["fun"](2)
{
	标识符num=2
	标识符n=1
	print(n)输出1
	return {"fun": lambda x:fun(x,num)}

	B2>>>type_fun(0)["fun"](1)["fun"](2)["fun"](3)
	{
		标识符num=2
		在本作用域未找到n的值,在外层作用域用寻找n=1,并作为参数传给fun()函数
		return {"fun": lambda x:fun(,)}
	}
}

B4>>>type_fun(0)["fun"](1)["fun"](2)["fun"](3)
{
	标识符num=3
	标识符n=2
	print(n)输出2
	return {"fun": lambda x:fun(3,2)}
}



#情形3
type_c=fun(0)["fun"](1)
type_c["fun"](2)
type_c["fun"](3)
	
C1>>>type_c=fun(0)
	{
		标识符num=0
		标识符n=None
		print(n)输出None
		return {"fun": lambda x:fun(x,num)}
		C1>>>type_c(0)["fun"](1)
		{
			标识符num=1
			未找到n的值,在外层作用域用寻找n=0,并作为参数传给fun()函数
			return {"fun": lambda x:fun(1,0)}
		}
	}
C2>>>type_c(0)["fun"](1)
{
	标识符num=1
	标识符n=0
	print(n)输出0
	return {"fun": lambda x:fun(x,num)}
	C2>>>type_c["fun"](2)
	{
		标识符num=2
		未找到n的值,在外层作用域用寻找n=1,并作为参数传给fun()函数
		return {"fun": lambda x:fun(2,1)}
	}

	C2>>>type_c["fun"](3)
	{
		标识符num=3
		未找到n的值,在外层作用域用寻找n=1,并作为参数传给fun()函数
		return {"fun": lambda x:fun(3,1)}
	}
}	
	
C3>>>type_c)["fun"](2)	
{
	标识符num=2
	标识符n=1
	print(n)输出1
	return {"fun": lambda x:fun(x,num)}
}
	
	
C4>>>type_c["fun"](3)	
{
	标识符num=3
	标识符n=1
	print(n)输出1
	return {"fun": lambda x:fun(x,num)}
}
	

1.3装饰器

  • 本质是回调和闭包的结合,其本身也是一种函数
  • 在装饰器下的函数,在调用时要在装饰器内部执行,不单单执行函数本身
  • 使用的格式:@装饰器自定义命名,将下面的函数作为参数放入装饰器中执行
def decorator(fun):
    def block():
        语句1
        fun()
        语句2
    return block
@decorator
def test():
    语句1
#被装饰的函数
test()#这里看起来是在调用test,实际是在调用装饰器里的闭包

1.3.1基本装饰器

自身不带参数

def decorator(fun):
    def block():
        print("装饰器开始使用")
        fun()
        print(f"装饰器结束使用")
    return block
@decorator
def test():
    print("test函数被调用")
test()
#输出 装饰器开始使用 test函数被调用 装饰器结束使用

1.3.2带参装饰器

带有参数的装饰器,带了两层闭包,是装饰器的“套娃”

  • 带参装饰器示例
def decorator(fun):
    def block(num):
        print("装饰器开始使用")
        fun()
        print(f"装饰器结束使用",num)
    return block
@decorator1()
def test():
    print("test函数被调用")
  • 被调用的函数调用时传参:
def decorator1(num):
    def decorator2(fun):
        def block():
            print("装饰器开始使用",num)
            fun()
            print("装饰器结束使用")
        return block
    return decorator2

@decorator1(3)
def test():
    print("test函数被调用")
test()
#输出
装饰器开始使用 3
test函数被调用
装饰器结束使用

1.3.3链装饰器

用装饰器来装饰装饰器装饰过的函数,和上面类似,但形式不同

def decorator1(num):
    def block(fun):
        def block1():
            print("装饰器开始使用",num)
            fun()
            print("装饰器结束使用",num)
        return block1
    return block
def decorator2(fun):
    def block():
        print("装饰器开始使用2")
        fun()
        print("装饰器结束使用2")
    return block
@decorator1(3)
@decorator2
def test():
    print("test函数被调用")
#输出
装饰器开始使用 3
装饰器开始使用2
test函数被调用
装饰器结束使用2
装饰器结束使用 3

2.面向对象编程

是一种通过组织对象来设计程序的编程方法

类>>>对象

2.1类与对象

2.1.1类Class

类是对一类对象的抽象,定义了对象的属性(变量)和方法(函数),如之间学过的list,tuple等都是官方的类

  • 数据成员:表明对象的特征。 相当于变量
  • 方法成员:表明对象的功能。 相当于函数
  • 通过class关键字定义类。
  • 类的创建语句语法:

class 类名 (继承列表):

实例属性(类内的变量) 定义
实例方法(类内的函数method) 定义
类变量(class variable) 定义
类方法(@classmethod) 定义
静态方法(@staticmethod) 定义

#定义示例
class animal:
	name='sakana'
	year=10
	def fun(arg):
		print("这是一个类方法(函数)",arg)
sakana=animal()
2.1.2对象

对象是类的实例化,是类的实际数据存储,具有类所定义的属性和方法。

  • 变量存储的是实例化后的对象地址

  • 类参数按照初始化方法的形参传递

    • 对象是类的实例,具有类定义的属性和方法。
    • 通过调用类的构造函数来创建新对象。
    • 每个对象有自己的状态,地址不同,但共享方法(类的内部)
class animal:
	name='sakana'
	year=10
sakana1=animal()
#调用对象的属性/方法
print(sakana1.name)

使用类创建对象时,可以使用官方方法init初始化成员的值:

#_init_这个函数是一个魔术方法
class Animal:
	#创建对象时调用:类名(参数)会传给函数_init_
	def _init_(arg,name,year):
		print(arg,name,year) #<__main__.Animal object at 0x000002A2893356A0> 空棘鱼 834
		#给对象s1添加新新属性,属性名是age
		arg.age=year-5    

#将"空棘鱼",834传入类的_init_函数
s1=Animal("空棘鱼"834)#这里有一个隐式操作,_init_(s1,"空棘鱼",834),和上面调用其他方法相同,对象会作为参数传入
print(s1.name,s1.year,s1.age) #空棘鱼 834 829

2.2.属性和方法

2.2.1实例属性

实例.属性

class Animal:
	type='fish'
	def __init__(self,name,year):
		print(self,name,year)
		#给对象s1添加新新属性,属性名是age
		self.name=name
		self.year=year
		self.age=year-5    

#将"空棘鱼",834传入类的_init_函数
s1=Animal("空棘鱼",834)
s2=Animal("阿姆斯鱼",194)
print(s1.type,s1.name,s1.year,s1.age) #fish 空棘鱼 834 829
print(s2.type,s2.name,s2.year,s2.age) #fish 阿姆斯鱼 194 189
2.2.2实例方法
class 类名(继承列表):
	def 实例方法名(self, 参数1, 参数2, ...): 
		语句块
  • 实例方法就是函数,至少有一个指向实例对象的形参self
class Num:
    num=0
    def __init__(self, num):
        print(num) #输出传入的num的值10 20
    def change_num(self, num):
        self.num=num
        print(num)
        
s1=Num(10)
s2=Num(20)
print(s1.num)
print(s2.num)
s1.num=30
print(s1.num) #直接修改Num属性num的值 30
s1.change_num(40) #调用Num的change_num方法修改num的值
print(s1.num) #40
print(s2.num) #s2的num值不变

实例方法内还可以调用其他方法(包括自己,此时就是递归)

class Num:
    num=20
    def __init__(self, num):
        print(num) #输出传入的num的值10 
    def fun1(self):
        #这里看似是在fun2定义之前调用了fun2方法,但是实际上在实例s1创建的时候就已经开辟了这个方法的空间
        self.fun2()
    def fun2(self):
        print(self.num)
s1=Num(10)
s1.fun1()
  • 自己写的一个综合示例
class Band:
    name="unkonwn"
    country = "unkonwn"
    year=0
    def __init__(self,name,country,year):
        self.name=name
        self.country=country
        self.year=year
    
    def is_new(self):
        if self.year>2010:
            return self.print()
        else:
            return 0

    def print(self):
        print(f"乐队{self.name}来自{self.country}成立于{self.year}年")

band1=Band("sakanaction","JAPAN",2007)
band2=Band("Chinesefootball","CHINA",2015)
band1.is_new()
band2.is_new()
2.2.3类属性

类属性是属于类的属性,不属于类的实例

  • 一般是对象公用的属性
  • 类可以使用,对象也可以使用
class Animal:
	type='fish'
	def __init__(self,name,year):
		print(self,name,year)
		#给对象s1添加新新属性,属性名是age
		self.name=name
		self.year=year
		self.age=year-5    

#将"空棘鱼",834传入类的_init_函数
s1=Animal("空棘鱼",834)
#类来访问属性
print(Animal.type)
2.2.4类方法

类属性是属于类的方法,不属于类的实例

  • 类方法需要使用**@classmethod装饰器**定义
  • 类方法至少有一个形参用于绑定类,约定为 cls
  • 类和该类的实例都可以调用类方法
  • 类方法不能访问此类创建的对象的实例属性
class Animal:
	type='fish'
	def __init__(self,year):
		print(self,year)
		#给对象s1添加新新属性,属性名是age
		self.age=year-5    
	#一般是类使用的方法,是一种隐式操作	
	
	def get_type1(self):
		print(self.type)

	@classmethod
	def get_type2(cls):
		print(cls.type)


#实例对象可以访问的属性和方法:type属性,age属性,get_type1()方法,get_type2()方法(需要传参)  
s1=Animal(834)
print(s1.age)
#类可以访问的属性和方法:
#print(Animal.age),报错,因为类没有age属性,age属性是实例对象s1的属性
print(Animal.type)
Animal.age=5
Animal.get_type1(Animal) #类可以调用普通方法,但是需要传入该类作为参数
#类使用类方法不需要传本身
Animal.get_type2()

一个使用类方法和普通方法创建单例的实例

2.2.5静态方法

定义在类的内部,作用域是类内部

  • 使用@staticmethod装饰器定义
  • 不需要self和cls参数
  • 可以通过类或类实例调用
  • 可以访问类属性,但不能访问实例属性
class Num:

    def fun1 (self):
        print("实例方法")
    @classmethod
    def fun2(cls):
        print("类方法")
    @staticmethod
    def fun3():
        print("静态方法")
    @staticmethod #这个静态对象通常当作一种工具
    def sigmond(x):
        return 1/(1+pow(2.71828,-x))
#类
Num.fun1(Num)
Num.fun2()
Num.fun3()
#静态方法需要传入参数
print(Num.sigmond(10))
#实例对象
n1=Num()
n1.fun1()
n1.fun2()
n1.fun3()
print(n1.sigmond(10))
2.2.6构造方法

构造方法__new__()

  • 负责对象的 创建 和内存分配的。
  • 在对象实例化时被调用,负责返回一个新的对象实例。
  • 通常不需要显式地定义 __new__() 方法,Python会调用基类 object 的 __new__() 方法。
class MyClass:
    def __new__(self,*args):
        print("调用 __new__ 方法,创建对象")
        return super().__new__(self) #super是一种继承

    def __init__(self, value):
        print("调用 __init__ 方法,初始化对象")
        self.value = value


# 创建对象
obj1=MyClass(10)
print(obj1)
2.2.7初始化对象

对象被创建后,Python会调用 __init__() 来初始化对象的属性。(前面已经学习)

2.2.8魔术方法

魔术方法是一种特殊的方法,用双下划线包裹,例如__init____str____add__等。这些方法允许用户自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。

常见魔术方法

重要:

  • __init__(self, ...): 初始化对象,通常用于初始化对象的属性。
  • __len__(self): 定义对象的长度,可通过len(object)调用。通常在自定义容器类中使用。
    class Images:
        def __init__(self, path):
            self.path = path
            self.arr=['1.jpg','2.jpg']
        def __len__(self):
            return len(self.arr)
    imgs=Images("./imgs")
    count=len(imgs)
    print(count)
    
  • __getitem__(self, key): 定义对象的索引操作,使对象可被像列表或字典一样索引。例如,object[key]
    class Images:
        def __init__(self, path):
            self.path = path
            self.arr=['1.jpg','2.jpg']
        def __len__(self):
            return len(self.arr)
        
        def __getitem__(self, index):
            return self.arr[index]
    
    obj1=Images("./imgs")
    count=len(obj1)
    print(count)
    print(obj1[0])
    
  • __setitem__(self, key, value): 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如,object[key] = value
    class Images:
        def __init__(self, path):
            self.path = path
            self.arr=['1.jpg','2.jpg']
        def __len__(self):
            return len(self.arr)
        
        def __getitem__(self, index): #取值
            return self.arr[index]
        
        def __setitem__(self, index, value): #赋值
            self.arr[index]=value
            print(index,value)
    
    obj1=Images("./imgs")
    count=len(obj1)
    print(count)
    print(obj1[0])
    # obj1[10]="Hello" #__setitem__(obj,10,"Hello"),列表越界
    obj1[1]=2 #__setitem__(obj,1,2)调用setitem修改下标1的值
    print(obj1[1]) #2
    
  • __iter__(self): 定义迭代器,使对象可迭代,可用于for循环。(后面单独学习)
  • __next__(self): 定义迭代器的下一个元素,通常与__iter__一起使用。(后面单独学习)
  • __call__(self, other) 是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。(后面机器学习中的预测会学到)
    class Images:
        def __init__(self, path):
            self.path = path
            self.arr=['1.jpg','2.jpg']
        
        def __call__(self,*args,**kwargs):
            print("调用__call__",args,kwargs)
    
    obj1=Images("./obj")
    print(callable(obj1)) #True,添加了call魔术方法后,就可以作为函数调用
    obj1(10,20,30,a=100,b=200)
    

一般:

  • __str__(self): 定义对象的字符串表示形式,可通过str(object)print(object)调用。如可以返回一个字符串,若要输出则会输出对应字符串而不是类型内存等。
  • __repr__(self): 定义对象的“官方”字符串表示形式,通常用于调试。可通过repr(object)调用。
  • __delitem__(self, key): 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如,del object[key]
  • __add__(self, other): 定义对象相加的行为,使对象可以使用+运算符相加。例如,object1 + object2
  • __sub__(self, other): 定义对象相减的行为,使对象可以使用-运算符相减。
  • __lt__(self, other): 定义对象小于其他对象的行为,使对象可以使用<运算符比较。
  • __gt__(self, other): 定义对象大于其他对象的行为,使对象可以使用>运算符比较。
  • __eq__(self, other): 定义对象相等性的行为,使对象可以使用==运算符比较。

2.3 OOP基本特性

OOP的四大基本特性是封装继承多态抽象

2.3.1 封装
  • 封装是指将对象的属性方法包装在一起,对外隐藏实现细节,只提供必要的接口给外部访问。

  • 通过__init__方法初始化属性,并通过方法来操作这些属性。

  • __开头的属性或方法是私有的,在子类和类外部无法直接使用

  • 可使用“私有”属性和“公有”方法控制外部访问。

    class Images:
        def __init__(self, path):
            self.path = path
            self.arr=['1.jpg','2.jpg']
        def __len__(self):
            return len(self.arr)
        
        def __getitem__(self, index):
            return self.arr[index]
    
    obj1=Images("./imgs")
    count=len(obj1)
    print(count)
    print(obj1[0])
    
2.3.2继承

两个或多个类之间的关系,父类的属性和方法可以由子类继承

2.3.2.1概念
  • 继承/派生

    • 继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。
    • 派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为
  • 继承/派生的作用

    • 用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。
    • 在不改变基类的代码的基础上改变原有类的功能
  • 继承/派生名词:

    • 基类(base class)/超类(super class)/父类(father class)
    • 派生类(derived class)/子类(child class)
2.3.2.2继承实现

继承语法:

class 类名(父类1,父类2…)

#这是一个单例继承,若父类有多个,则为多类继承
class Animal:
    def type_num(self):
        self.num=500

class Dog(Animal):
    def type_num(self):
        self.num=1000

class fish(Animal):
    def type_num(self):
        self.num=2000

2.3.2.3覆盖

也是重写,即子类中与父类相同的方法,但实现不同的功能

class Animal:
    def __init__(self,name):
        self.name=name

    def type_num(self):
        self.num=500
        print(self.num)

    def size(self):
        self.size=20
        print(self.size)

class Fish(Animal):
    def __init__(self,name,age):
        #调用父类的构造函数
        super().__init__(name)
        self.age=age

    def type_num(self):
        self.num=100
        print(self.num)

fish1=Fish('sakana',5)
#调用的父类Animal的size方法
fish1.size() #20
#Fish类覆盖了父类的type_num方法
fish1.type_num() #100
2.3.3多态

是指同一个方法在不同对象上的不同功能体现,使用继承和重写来实现

class Band:
    country="Japan"  
    def Name(self):
        self.name="sakanaction"
        print(self.name)

class Vocal(Band):
    def Name(self):
        self.name="山口一郎"
        print(self.name)
    
class Guitar(Band):
    def Name(self):
        self.name="岩寺基晴"
        print(self.name)

class Band_name(Band):
    def Found_year(self):
        self.year="2018"
        print(self.year)

vocal=Vocal()
#重写了Name方法
vocal.Name() #山口一郎
guitar=Guitar()
#重写了Name方法
guitar.Name() #岩寺基晴
band=Band_name()
band.Name() #sakanaction
#调用了父类的属性
print(band.country) #Japan
2.3.4重载

也是重写,就是使用前面的魔术方法,来重载对象转字符串,内建函数,运算符

2.3.4.1 对象转字符串重写
  • 对象转字符串函数重写方法

    • str() 函数的重载方法:

      • def __str__(self)

        • 如果没有 __str__(self) 方法,则返回repr(obj)函数结果代替
  • str/repr函数重写示例

    class MyNumber:
        """此类用于定义一个自定义的类,用于演示str/repr函数重写"""def __init__(self, value):
            """构造函数,初始化MyNumber对象"""
            self.data = value
    ​
        def __str__(self):
            """转换为普通字符串"""
            return "%s" % self.data
    ​
    ​
    n1 = MyNumber("一只猫")
    n2 = MyNumber("一只狗")
    print("str(n2) ===>", str(n2))
    
2.3.4.2 内建函数重写
  • __abs__ abs(obj) 函数调用

  • __len__ len(obj) 函数调用

  • __reversed__ reversed(obj) 函数调用

  • __round__ round(obj) 函数调用

  • 内建函数 重写示例

    # file : len_overwrite.py
    class MyList:
        def __init__(self, iterable=()):
            self.data = [x for x in iterable]def __repr_(self):
            return "MyList(%s)" % self.data
    ​
        def __len__(self):
            print("__len__(self) 被调用!")
            return len(self.data)def __abs__(self):
            print("__len__(self) 被调用!")
            return MyList((abs(x) for x in self.data))
    ​
    ​
    myl = MyList([1, -2, 3, -4])
    print(myl.data)
    print(len(myl))
    print(abs(myl).data)
    
2.3.4.2 运算符重载
  • 运算符重载是指让自定义的类生成的对象(实例)能够使用运算符进行操作

  • 运算符重载的作用

    • 让自定义类的实例像内建对象一样进行运算符操作
    • 让程序简洁易读
    • 对自定义对象将运算符赋予新的运算规则
  • 运算符重载说明:

    • 运算符重载方法的参数已经有固定的含义,不建议改变原有的意义
方法名运算符和表达式说明
__add__(self, rhs)self + rhs加法
__sub__(self, rhs)self - rhs减法
__mul__(self, rhs)self * rhs乘法
__truediv__(self, rhs)self / rhs除法
__floordiv__(self, rhs)self // rhs地板除
__mod__(self, rhs)self % rhs取模(求余)
__pow__(self, rhs)self ** rhs
2.3.5对象的成员操作

对象在对成员进行操作时,在取值和赋值的情况下的操作也不同

class Band:
    country="Japan"  
    def Name(self):
        self.name="sakanaction"
        print(self.name)

class Vocal(Band):
    def Name(self):
        self.name="山口一郎"
        print(self.name)
    

vocal=Vocal()
#重写了Name方法
vocal.Name() #山口一郎
#取值操作,本类里没有该成员,故去父类的成员里找
print(vocal.country) #Japan
#赋值操作,该类里没有该成员,则直创建并赋值
vocal.born_place='北海道'
print(vocal.born_place) #北海道

综合实例

class Band:
    def __init__(self, name):
        self.name = name
        print(self.name)

    def play_song(self, song):
        print(f"{self.name} is playing '{song}'")
class Song(Band):
    def __init__(self, name, members):
        self.name = name
        self.members = members
        print(f"{self.name}由成员{self.members}组成")

    def play_song(self,song,num):
        print(f"{self.members[num]} is playing '{song}'")

    def album(self,name):
        self.al_name=name
        print(f"{self.mu_name} 来自专辑'{self.al_name}'")
       
band=Song("sakanaction",["山口一郎","岩寺基晴","冈崎英美","江岛启一","草刈爱美"])
band.play_song("Eureka",1)
2.3.6私有属性

有一部分属性只能在类的内部使用,称为私有属性,__x来定义私有属性

#__属性名
class Animal:
    __x=100
    def fun(self):
        print(self.__x)

s=Animal()
print(s.__x) #报错,'Animal' object has no attribute '__x'
s.fun() #100,可以通过函数来调用私有属性,有点类似于闭包的思想

2.4.super函数

用于调用父类的一种方法

2.4.1实现实例

在类的内部调用父类方法:

class Band:
    country="Japan"  
    def Name(self):
        self.name="sakanaction"
        print(self.name)

    def  Country(self):
            print(self.country)

class Vocal(Band):
    #重写了父类的方法,但是想在父类已有的代码的基础上再添加一些代码
    def Name(self):
        super().Name() #super()表示调用父类,类似Band.Name(),依然是self在调用Name(),这种使得父类的Name方法和自己类的Name方法可以一起调用
        self.name="山口一郎"
        print(self.name)
    
vocal=Vocal()
vocal.Name() #只调用了重写的方法 输出:山口一郎   使用super()后输出:sakanaction 山口一郎
vocal.Country() #只调用了父类的方法

对象在类的外部调用父类方法:

class Band:
    def Name(self):
        self.name="sakanaction"
        print(self.name)
        
class Vocal(Band):
    def Name(self):
        self.name="山口一郎"
        print(self.name)
vocal=Vocal()
super(Vocal,vocal).Name() #sakanaction
#总结:
1.把对象vocal作为第一个参数传入self,执行子类Vocal的父类Band的Name函数
2.super(当前类,self)是标准写法,super()是自动推论过当前类和self对象后的简化
2.4.2 super().__init__()

通过 super(). __init__() 调用父类构造函数,以确保父类的构造函数被正确调用和初始化。

class Band:
    def __init__(self,name,year):
        self.name=name
        self.year=year
        print(name,year)
        
class Vocal(Band):
     def __init__(self,name,year,age):
		#在子类中调用父类的初始化对象的方法
        super().__init__(name,year)
        self.age=age
band1=Vocal('sakanaction',10,44)

使用**super()**的理由:

  • **代码重复:**直接调用父类的方法,避免代码的重复性
  • **正确初始化:**确保父类的初始化逻辑(如设置属性、分配资源等)被执行。

3.迭代器和生成器

3.1迭代器

**迭代器(Iterator)**是一种对象,实现了Python的迭代协议(__iter__()__next__() 方法)。通过迭代器,可以逐个访问容器中的元素。

3.1.1特性
  • 按需生成数据,节省内存。

  • 实现协议

    • __iter__():返回自身。
    • __next__():返回下一个元素;如果没有更多元素,则抛出 StopIteration 异常
  • 可迭代对象与迭代器不同:

    • 可迭代对象实现 __iter__() 方法,返回一个迭代器。

    • 迭代器既实现 __iter__() 又实现 __next__(),而迭代器不能使用next方法(自定义除外)。

    • 迭代器只能迭代一次,而可迭代对象可以多次迭代

  • iter函数会返回一个可迭代对象的迭代器

list1=[10,20,30]
iiterator1=iter(list1)
#使用next方法迭代下一个数据
print(next(iiterator1)) #10
#迭代器只会往前取,不会后退
for i in range(2):
    print(next(iiterator1),"迭代了") #20 迭代了 30 迭代了
#没有结果,只能迭代一次
for i in range(2):
    print(next(iiterator1),"迭代了")

for i in list1:
    print(i)
#列表本身也是个迭代器,但可以多次迭代
for i in list1:
    print(i)
3.1.2自定义迭代器
#类中实现了__iter__()和__next__()方法,用这个类创建的对象就是一个可迭代对象
#__iter__():1.要求返回一个迭代器对象2.iter函数调用传入用这个类创建的对象,就会调用__iter__()方法,来返回一个迭代器对象
#__next__():函数要求返回数据,使用next函数调用传入一个迭代器对象,就会调用__next__()方法,来返回数据

class Boom:
    def __init__(self, x):
        self.x = x
    def __iter__(self):
        return self
    def __next__(self):
        if self.x > 0:
            self.x -= 1
            return self.x
        else:
            #抛出异常
            raise StopIteration
b1= Boom(5)
iterator1 = iter(b1)
for i in iterator1:
    print(i)

3.2生成器

生成器(Generator是一种特殊的迭代器,通过函数定义,用 yield 语句生成值。生成器可以自动实现迭代协议,无需手动实现 __iter__()__next__()

3.2.1 特性
  1. 简洁:比手动实现迭代器更易写。
  2. 惰性计算:生成器在每次调用 next() 时生成一个值,而不是一次性生成所有值。
  3. yield暂停函数执行并返回值,保留函数的状态,以便下一次继续执行。
def fun():
    print("函数运行1")
	#暂停了程序,yield将函数的语句块的结果放入一个迭代器中
    yield "块1"
    print("函数运行2")
    yield "块2"
    print("函数运行3")
    print("函数运行3")
    yield "块3"
    print("函数结束运行")
re=fun()
print("函数外部") 
print(next(re))
print(next(re))
print(next(re))
print(next(re))
#输出
函数外部
函数运行11
函数运行22
函数运行3
函数运行33
函数结束运行

#使用for循环简化代码
def fun():
    data=['a','b','c','d']
    for i in data:
        yield i
re=fun()
print("函数外部1")
for i in re:
    print(i)

一个例子来理解range方法的实现

def Myrange(start,end,step):
    i=start-step
    while True:
        i+=step
        if i>=end:
            raise StopIteration
        yield i
        
re=Myrange(2,10,2)
for i in re:
    print(i) #raise StopIteration
3.2.2生成器表达式

基本语法:

(表达式 for 变量 in 可迭代对象 [if 真值表达式])#[]的内容可省略

示例:

gen_expr = (x**2 for x in range(5))
print(next(gen_expr))  # 0
print(next(gen_expr))  # 1
3.2.3课堂练习
#用生成器实现斐波那契数列
def fibonacci(n):
    a,b=0,1
    for _ in range(n):
        yield b
        a,b=b,a+b
fib_list=fibonacci(10)
for i in fib_list:
    print(i,end=" ") #1 1 2 3 5 8 13 21 34 55 
3.2.4应用场景
  • 数据流处理:处理大文件或流式数据,避免内存耗尽。
  • 无限序列生成:如斐波那契数列、素数序列。
  • 管道化数据处理:与 itertools 模块结合使用。

3.3区别对比

特性迭代器生成器
实现通过类实现,手动定义方法使用函数和 yield 定义,自动实现迭代协议
代码简洁性代码较复杂代码简单
状态管理手动管理状态自动保存函数的运行状态
惰性计算支持支持
示例应用自定义复杂的迭代逻辑简单的数据流生成

4.异常处理

  • 用于提示上层语句有错误,可能会导致程序无法继续执行

  • 使用异常处理的语法来继续执行后面无错的代码,来增强程序的鲁棒性(健壮性/robustness)

4.1 try语句

  • 语法:

    try:
    	可能出错的语句
    
    except 错误类型 :
    	处理错误
    
    except 错误类型 :
    	处理错误
    
    except 错误类型 :
    	处理错误
    
    ......
    else:
    	 没有错误时执行,处于异常时不执行,可不写
    
    finally:
        最终的处理语句,无论如何都会执行,可不写
    
  • 示例

    def fun1(num):
         print(int(num))
    def fun2(num):
         print(num)
    try:
        fun1("sakana")
    except ValueError:
        print("该值不能转换为整数")
    fun2(50) #出错了,但是后续代码中的fun2函数仍然执行
    #输出
    该值不能转换为整数
    50  
    

4.2 raise语句

  • 语法:

    raise 异常类型: / raise 异常对象:
    
  • 示例:

    def get_band():
        x = int(input('请输入乐队成员数量:'))
        if 3 <= x <= 6:
            return x
        # raise ValueError
        raise ValueError('乐队数量不在3-6之间')
    
    try:
        band = get_band()
        print(band)
    except ValueError as err:
        print("成绩输入有误 err=", err)
    

4.3错误类型

错误类型说明
ZeroDivisionError除(或取模)零 (所有数据类型)
ValueError传入无效的参数
AssertionError断言语句失败
StopIteration迭代器没有更多的值
IndexError序列中没有此索引(index)
IndentationError缩进错误
OSError输入/输出操作失败
ImportError导入模块/对象失败
NameError未声明/初始化对象 (没有属性)
AttributeError对象没有这个属性
<!-- 以下不常用 -->
GeneratorExit生成器(generator)发生异常来通知退出
TypeError对类型无效的操作
KeyboardInterrupt用户中断执行(通常是输入^C)
OverflowError数值运算超出最大限制
FloatingPointError浮点计算错误
BaseException所有异常的基类
SystemExit解释器请求退出
Exception常规错误的基类
StandardError所有的内建标准异常的基类
ArithmeticError所有数值计算错误的基类
EOFError没有内建输入,到达EOF 标记
EnvironmentError操作系统错误的基类
WindowsError系统调用失败
LookupError无效数据查询的基类
KeyError映射中没有这个键
MemoryError内存溢出错误(对于Python 解释器不是致命的)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxError Python语法错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
以下为警告类型
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值