python全栈开发中级_python全栈开发中级班全程笔记(第三模块、第一章(多态、封装、反射、内置方法、元类、作业))...

python全栈开发笔记第三模块

第二部分:面向对象进阶

一、类的多态与多态性(重点)

1、多态:指的就是同一类事物有多种形态

2、多态性,又称多态动态绑定,在继承背景下使用时,有时也称为多态性,

是指不考虑实例类型的情况下,使用实例,分为静态实例(如:很多类型都可以使用“+”这个符号)与动态实例

class Animal: #定义动物的形态

def run(self): #定义走

pass

class People(Animal): #定义人的形态

defrun(self):print("sey hello!")class Pig(Animal): #定义猪的形态

defrun(self):print("sey hengheng")class Dog(Animal): #定义狗的形态

defrun(self):print("sey wangwang")classCat(Animal):defrun(self):print("sey miaomiao!!")

peo=People()

pig=Pig()

dog= Dog() #定义各自的形态

cat =Cat()

peo.run()

pig.run()

dog.run()#不考虑实例而使用实例

还可更简单一步(拓展性)def func(name): #定义一个接口,

name.run()

func(peo)

func(pig)

func(dog)

func(cat)

View Code

***它的好处:

①、增强了程序的灵活性,使程序更简洁 func(name)去调用,但必须是多态化代码

②、增强程序的拓展性,如再增加一个猫类形态,不需要更改调用接口,只需增加猫类的属性和实例化方式。就可以使用

3、其实 python 崇尚的并不只是上面的拓展性, python 崇尚的《鸭子类型》

《鸭子类型》:考察一种动物,走路像鸭子,叫声像,造型像,形态也像鸭子,就被定义为鸭子(是一种概念)

在类中:如果没有继承关系,只是模拟的像父类,那他就是父

class File: #定义文件类,实现读写方法

defread(self):print("open's read")defwrite(self):print("open's write!")class Disk: #定义磁盘类,实现读写方法

defread(self):print("read disk")defwrite(self):print("disk is write!!")class Txt: #定义文本档, 实现读写功能

defread(self):print("txt is read")defwrite(self):print("write is txt")

file=File()

disk=Disk()

txt= Txt() #把三类实例化

def itea(name): #虽然三者没有继承关系,也不属于同一类, 但使用方法相同或相似,就可以定义个接口,以供调用

name.read()

name.write()

itea(file)

itea(disk)#只传进去各个的类名,在更简洁的代码下,就可以实现相同的功能

itea(txt)#这就是多态性的优势所在(可以为不同的类设置相同的调用方法(前提是:必须有相同的调用方式)

View Code

*#*#*#*#这就是多态性的优势所在(可以为不同的类设置相同的调用方法(前提是:必须有相同的调用方式)

二、类的封装与封装意义

封装:字面意思就是把某种物品或东西,通过隐藏和整合包装,使其更具可观性或可用性

1、封装之隐藏属性

class A: #隐藏属性的写法(字符前加上__)

__ppx = 1 #_A__ppx = 1 (后台执行时的名称)

def __init__(self, name, age):

self.__name = name #self._A__name = name

self.__age = age #self._A__age = age

def __foo(self): #_A__foo(self):

print("run.__foo")def dir_name(self): #调用时,也自动更改了调用方式

self.__foo() #后台执行的代码:self._A__foo()

print("my is dir_name")

a= A("will", 18)print(a.__ppx) #报错发现不存在

print(a.__name)print(a.__age, a.__sex)print(a.__foo) #成功实现所有属性的隐藏

print(A.__dict__) #通过名称空间查看内部发生了什么变化

print(a.__dict__)

a.dir_name()#发现命名和类的名称空间结构已经发生变化,实现属性隐藏

View Code

发现命名和类的名称空间结构已经发生变化,实现属性隐藏

它的特点:

封装了类,使外部无法正常调用

在类内部可以正常调用(它的作用主要对外封闭隐藏,但是内部可以正常调用)

子类无法覆盖父类__开头的属性详情看下面例子

例:

classFoo:

dog= "dog"

def func(self): #没有加 __的属性

print("form is func")def __foo(self,): #加了 __的属性

print("form foo")classFunc(Foo):def __foo(self):print("form foo 111111")

f=Func()

f.func()#调用继承的不加的属性

f.__foo() #调用加了__的属性(发现无法覆盖, 在定义类之初,类内部存在__的属性,就会使类的调用方法发生变化,所以,他不会覆盖父类的属性,只是增加了自己的属性)

View Code

*#*#*#*#*#*#综合以上,总结一:

这种属性隐藏只是个障眼法(不会真正意义上达到隐藏,只不过在定义类时,自动检测语法后,更改了类名的调用方式)

python 并不会真的限制,而只是更改了调用方式(_类名__属性),虽然这样也能访问到,但如果隐藏了还调用,

那就不要去隐藏了,这种方法也没有意义

classB:__xx = 1

print(_B__xx)def __init__(self, name):

self.__name =name

B.__y = "alex"b= B("will")

b.__f = "f"

print(B.__dict__) #发现在定义类体完成之后,再增加的属性不会被隐藏

print(b.__dict__)print(b.__f)print(B.__y) #发现都可以正常调用

View Code

总结二 :这种变形只有在定义类体的时候发生一次改变。在有赋值操作的时候,是不会再改变的

验证问题3:实现类体内部自己调用自己,不使用其他的同名变量函数

#验证问题3:实现类体内部自己调用自己,不使用其他的同名变量函数

classK:def __foo(self):print("form is K foo")defbar(self):print("form is bar")

self.__foo()classM(K):def __foo(self):print("form is M foo")

m=M()

m.bar()

View Code

总结三 : 在继承中,如果不想让子类覆盖自己的方法,可以将方法定义为私有

*#*#*#*#*#*#

2、封装的意义

封装属性,分为 ① 封装数据属性 ② 封装与方法属性

(1)封装数据属性:明确的区分内外,控制外部对隐藏属性的操作行为

class People: #定义一个类

def __init__(self, name, age): #设置隐藏属性

self.__name =name

self.__age =agedef _format(self): #定义接口并把隐藏属性定义规则(使其他人无法修改)

print("name:, age:" % (self.__name, self.__age))def alter(self, name, age): #为了方便使用, 定义一个修改名称接口,便于多人调用

if not isinstance(name, str): #可以为其设置条件,

print("input error! is not str")return

else:

self.__name =nameif notisinstance(age, int):print("input error! is not int!")return

else:

self.__age =age

p= People("will", 25)

p._format()

p.alter(4, 3)

p._format()

View Code

(2)封装方法属性:隔离复杂度(把复杂的问题隔离,实现调用环节简单化)

classATM:def __plug(self):print("插卡")def __user(self):print("用户认证")def __money(self):print("取款金额")def __print(self):print("print bill")def __exit(self):print("exit card")defoperate(self):

self.__plug()

self.__user()

self.__money()

self.__print()

self.__exit()

a=ATM()

a.operate()

View Code

三、封装的可扩展性封装的可扩展性非常高,可以给用户定义该看到的东西和没必要看的东西,

class Room: #定义一个房子类

def __init__(self, name, owner, weight, length, height): #再次增加一个需求

self.name =name

self.owner=owner

self.__weight = weight #把计算面积的数值隐藏

self.__length =length

self.__height =heightdef area(self): #后台计算方式封装成调用接口

sum = int(self.__weight) * int(self.__length) * self.__height

print("房间空间为 %sm³" %sum)

r= Room("alex", "厨房", 10, 4, 5)

r.area()#实现查看房屋面积功能(也实现了调用体积的功能)#更改了内部属性,但没有更改调用方式, 这就是可扩展性

View Code

*#*#*#*#更改了内部属性,但没有更改调用方式, 这就是可扩展性

四、类的装饰器

1、用一个计算成人 BMI 指数的方法简述装饰器的作用

小于18.5 = 太轻

18.5 - 23.9 = 正常

24 - 27 = 过重

28 - 32 = 肥胖

大于 32 = 超胖

体征指数(BMI) = 体重(kg)÷ 身高的2次方(米)

classPeople:def __init__(self,name, weight, height):

self.name=name

self.weight=weight

self.height=height

@property#加上这个功能,相当于在调用时执行 p.bmi 等同于执行 p.bmi()

defbmi(self):

iminit= self.weight/(self.height ** 2)print("your's BMI %s" %iminit)returnp= People("will", 100, 1.81)

p.bmi#发现 访问数据属性,还要加括号

p.height= 1.88p.bmi#小结: property 的封装方法主要应用于正常访问不加括号,但是后台又必须加括号才能执行的地方

View Code

*#*#*#*#*#*小结: property 的封装方法主要应用于正常访问不加括号,但是后台又必须加括号才能执行的地方

2、property 的拓展功能

classPeople1:def __init__(self, name):

self.__name =name

@property#封装调用函数时不加括号

defname(self):print("get name")return self.__name@name.setter#把 property 封装的函数当做装饰器调用, 修改装饰器

defname(self, val):print("revise")if notisinstance(val, str):print("input error!!!")return

else:

self.__name =val

@name.deleter#封装的删除装饰器

defname(self):print("delete")print("not is delete!!")

p= People1("will")

p.name= "pps"

print(p.name) #成功实现查询、修改、删除的属性

del p.name

View Code

五、绑定与非绑定方法的介绍与使用在类内部定义的函数分为 2 大类:绑定方法 与 非绑定方法1、绑定方法:绑定方法绑定给谁,就由谁来调用,谁调用就会把谁当做第一个参数自动传入① 绑定到对象的方法:在类内部定义且没有被装饰器修饰的函数② 绑定到类的方法:在类内部定义且被装饰器 @cllassmethod 修饰的函数2、非绑定方法:非绑定方法是指:既不与类绑定也不与对象绑定,也没有自动传值,对象和类都可使用,这就涉及到@staticmethod 方法

classAtm:"""验证绑定对象方法"""

def __init__(self, name):

self.name=namedeftell(self):print("name is %s" %self.name)

f= Atm("will")print(Atm.tell) #类的函数类型

print(f.tell) #绑定对象的方法

classAtm:"""验证绑定到类的方法"""@classmethod#新的语法,在类内部调用类并自动传值

deffunc(cls):print(cls)print(Atm.func) #发现调用并绑定了自己

print(Atm)classAtm:"""非绑定方法的额验证"""

def __init__(self, name):

self.name=name

@staticmethod#非绑定方法的调用

deftell(x,y):print(x+y)

f=Atm

f.tell(3, 5) #绑定给对象

Atm.tell(5, 8) #类自己调用

View Code

3、绑定方法与非绑定方法的使用方法与使用场景

#定义一个类,查看每个人的用户信息

classPeople:def __init__(self, name, age, sex):

self.id= self.class_id() #高深之处,先绑定类内的函数再定义函数

self.name =name

self.age=age

self.sex=sexdef tell_info(self): #绑定到对象的方法

"""绑定对象方法"""

print("name:%s age:%s sex:%s" %(self.name, self.age, self.sex))

@classmethoddef form_conf(cls): #需求升级,从文件里导入资料并和类绑定匹配

"""绑定类的方法"""obj=cls(sett.name,

sett.age,

sett.sex)returnobj

@staticmethoddef class_id(): #非绑定参数的 实现(独立的不需要传值,也不需要依靠别的)

m = hashlib.md5(str(time.time()).encode("utf-8"))return m.hexdigest()

View Code

六、反射:通过字符串,映射到对象属性

#定义一个类

classPeople:def __init__(self, name, age):

self.name=name

self.age=agedefprints(self):print("name %s age is %s" %(self.name, self.age))

obj= People("will", 22)

obj.prints()

View Code

1、映射需要掌握的知识点:

①hasattr:hasattr(obj(对象),"name"(字符串形式的属性)) 判断属性(布尔类型返回 True 或 False )

#1、 hasattr(obj(对象),"name"(字符串形式的属性)) 判断属性(布尔类型返回 True 或 False )

print(hasattr(obj, "name")) #查看对象内部的名称空间(__dict__)内有没有对应的 key

print(hasattr(obj, "prints")) #或者说查看绑定类的名称空间内有没有定义或对应的变量名

View Code

② getattr:getattr(obj(对象), "name"(属性), None(如果查找不到,需返回的值,不填会报错)) 获取对象名称空间内的属性值

print(getattr(obj, "name", "ppt")) #找得到,返回对应值,

print(getattr(obj, "namexxx", "ppt")) #找不到,返回最后的值(ppt)

print(getattr(obj, "prints", "ppt")) #查找类内属性会返回对应的绑定方法的函数

View Code

③ steattr:setattr(obj(对象), "sex"(要增加的属性名(key)),"man"(属性名对应的值(value)) )修改属性方法

print(setattr(obj, "sex", "man")) #等同于 obj.sex = "man"

print(obj.sex)

View Code

④ delattr :delattr(obj(对象), "age"(要删除的属性名))

delattr(obj, "age") #等同于 del obj.age

print(obj.__dict__) #发下已经被删除

View Code

*#*#*#*#*#*# 小结:以上4种方法,也同样适用于类,可以达到类和对象的增删改查效果,

2、以上知识点的应用场景:

#这几个知识的应用广场景

classService:"""反射的应用场景"""

defrun(self):whileTrue:

inp= input(">>>:").strip()

cmds=inp.split()if hasattr(self, cmds[0]): #怎么通过判断用户输入的正确度去执行代码

func =getattr(self, cmds[0])

func(cmds)defget(self, cmds):print("get 功能", cmds)defput(self, cmds):print("put.....", cmds)#通过 hasattr 和 getattr 两个功能实现属性准确度判断

s =Service()

s.run()#需求升级,想通过输入的字符执行相对功能并下载#通过相同方法,把输入的字符串格式化成列表,实现最终目的

View Code

七、面向对象常用的内置方法

1、item 系列:(__setitem__, __getitem__, __delitem__)把对象模拟成调用字典的形式操作

#一、item系列:(__setitem__, __getitem__, __delitem__)把对象模拟成调用字典的形式操作

classPeople:def __init__(self, name):

self.name=namedef __getitem__(self, item):"""get:获取属性(查询时会用到)"""

return self.__dict__.get(item) #设置方法返回调用所获值

def __setitem__(self, key, value):"""set:修改与增加属性(修改和增加时会用到)"""

#print("setitem")

#print(key, value)

self.__dict__[key] = value #实现真正操作的一步

def __delitem__(self, key):"""del:删除属性(删除时用得到)"""

print("delitem")print(key)

self.__dict__.pop(key) #删除操作

del self.__dict__[key] #效果同上

obj= People("will")#验证get获取和查看属性

print(obj["name"])

obj["name"]#验证set修改与增加设置方法#需求,增加一个属性

obj.age = 18 #之前的方法可以这样写

obj["sex"] = "man" #用字典的方法这样写也可以实现

print(obj.sex)print(obj.__dict__)#验证 del 删除属性

del obj["name"]print(obj.__dict__)

View Code

2、__str__方法: 设置对象返回类型,使其转换成str类型,实现定制化

classPeople:def __init__(self, name, age):

self.name=name

self.age=agedef __str__(self):"""调用对象时,设置返回类型为 str

需要注意的是:必须以字符串类型返回,否则会报错"""

return "hi !! my name's %s. age %s" %(self.name, self.age)

p= People("will", 22) #实例化后

print(p) #打印对象时, 会触发 __str__ 系统自动识别并调用类内的 __str__ 属性

View Code

3、__del__方法:在调用结束后,自动释放内存

classOpen:def __init__(self, filename):print("读取文件.......")

self.filename=filenamedef __del__(self):"""这个方法会在程序结束后,自动触发并释放内存

这里面一般会写入跟对象相关的其他程序或资源"""

print("__del__资源回收")

f= Open("sett.py") #实例化

del f #如果提前删除 变量,也会触发类内定义的 __del__操作#(一般定义了__del__也不会 刻意去删除,只是验证下)相比手动操作当然还是自动删除比较好

print("__main__") #打印或者再干点其他事,发现类内部定义的 __del__在程序最后才运行,证明是执行的最后一波操作

#小结:如果在类内部定义了 __del__ 方法,所有类内部定义的属性,都是自动绑定给对象的,#这个方法在对象被删除的时候,会自动先触发这个方法的执行

View Code

4、其他内置方法:

(1) isinstance(obj, class):检测 obj 是否为 class 的对象

(2) issubclass(sub, super):检测 sub 是否为 super 的派生类/子类(3)描述符(__grt__, __set__, __delete__):描述符是新式类中(__grt__, __set__, __delete__)至少实现一个, 也被称为描述协议① __get__() :调用一个属性时触发② __set__() : 为一个属性赋值时触发③ __delete__() : 用 del 删除属性时触发

八、元类的介绍:

1、exec 方法的使用

(1)exec 方法的三个特性

① 执行字符串的命令

② 全局作用域(字典形式),如果不指定,默认使用 globals()

③ 局部作用域(字典形式),如果不指定,默认使用 locals()

g ={"x": 1,"y": 2}

f={}#exec :把 exec 内的字符串当做函数执行命令,可修改全局作用域的变量,也可创建局部作用域变量

exec("""global x, s

x = 15

s = 33

z = 3""", g, f) #exec(要执行的命令(字符串形式), 执行命令的作用域,g = 全局, f = 局部)

#print(g)

print(f)

View Code

2、一切皆对象,对象的用法

① 都可以被引用,x = obj(函数或类名都可以被当做一个变量值)

② 都可以当做函数的参数传入,

③ 都可以当做函数的返回值,

④ 都可以当做容器的元素(如:f = [func, class, item, obj]列表内的值,字典内的值,元祖内的值, 集合内的值等等...)

*#*#*#*# 一切皆对象的定义,就是以上四个点,反过来看,符合以上4个点的要求,就是对象,类也是对象,对象本身也是对象

classPeople:passp=People()print(type(p))print(type(People))print(type(print))

View Code

3、元类的定义方法实践证明,所有用 class 定义的类,都叫"type"类型能查询类型的 type()这一种类型,也是一个类,默认所有用 class 定义的类,它们的元类都是 type定义类的方式有 2 种:(1)、class 定义的类

#1、class 定义的类

classChinese:"""之前定义类的方法"""country= "china"

def __init__(self, name, age):

self.name=name

self.age=agedeftalk(self):print("%s is talking" %self.name)

obj= Chinese("will", 18) #实例化

print(obj, obj.name, obj.age)print(Chinese)

View Code

(2)、用 type 产生的类, 且不用 class 定义(需要符合定义三要素)

① 有类名

② 基类,如果继承就会有一个或多个,如果不继承则默认继承 object

③ 类的名称空间

#定义类的三要素

class_name = "Chinese" #三要素之一:类名

class_bases = (object, ) #三要素之二:基类,如果继承,会有一个或多个,如果不继承,会有一个默认值( object )

class_body = """country = "china"

def __init__(self, name, age):

self.name = name

self.age = age

def talk(self):

print("%s is talking" % self.name)"""class_dict={}

class_room= exec(class_body, globals(), class_dict) #三要素之三(重点):类的名称空间#print(class_dict) # 打印验证效果发现名称空间已经生成

Chinese1 =type(class_name, class_bases, class_dict)#print(Chinese1)

obj1 = Chinese1("will", 18) #实例化

print(obj1, obj1.name, obj1.age)

View Code

*#*#*#*#*#*# 小结:

一切皆对象:以前定义的所有变量对应的值,都是对象,包括用 class 定义的类,还有对象本身,也是对象。

不用 class 定义类用type 定义类需要三要素 ① 类的名称,② 类的基类参数,③ 类的名称空间

4、自定义元类控制类的创建行为与抛异常的解析

(1)通过定义元类,控制类的行为

如果定义一个父类,classMy_mete(type):"""自定义元类,也需要继承 type"""

def __init__(self, class_name, class_bases, class_dict): #定义 __init__ 就要继承之前基类所拥有的功能

if not class_name.istitle(): #在元类内部,可以定义其他框架(判断如果类名的首字母不是大写,触发报错机制)

raise TypeError("类名的首字母必须大写,")elif "__doc__" not in class_dict or not class_dict["__doc__"].strip(): #定义元类的其他内置方法

raise TypeError("定义类,必须注释且注释不能为空")

super(My_mete, self).__init__(class_name, class_bases, class_dict) #所以,需要继承基类所拥有的功能且增加自己创建的功能

class Chinese(object, metaclass=My_mete): #不写继承的基类,也有默认继承,后面的 metaclass = type 也是默认值

"""这样写,相当于 Chinese = My_mite(class_name, class_bases, class_dict),也就相当于基类内必须有 __init__ 的方法"""country= "china"

def __init__(self, name, age):

self.name=name

self.age=agedeftalk(self):print("%s is talking" % self.name)

View Code

(2) raise 抛异常语法

平时都是报错了才发现有异常,而用此方法之后,可以主动抛异常(就是不报错也可以出发异常)

class Chinese(object, metaclass=My_mete):

country= "china"

def __init__(self, name, age):

self.name=name

self.age=agedeftalk(self):print("%s is talking" %self.name)print(Chinese.__dict__)

View Code

5、义元类控制类的实例化行为与 __call__ 详解

(1) __call__方法

#本节知识储备点:__call__ 方法

classFoo:def __call__(self, *args, **kwargs): #定义函数传参的功能(所有类型)

"""定义此方法后,对象名加括号(obj()),也可以被调用"""

print(self)print(args)print(kwargs)

obj=Foo()

obj(1, 2, 10, a=1, b=2, c=5) #obj() 加括号,也可以调用,需要在类内部增加 __call__ 函数,#等同于调用 obj.__call__(obj, 1, 2, 10, a=1, b=2, c=5) 的调用方法#用这样的方法,就可以把对象变成一个可调用对象,且可修改调用对象方法#元类内部也有同样的内置方法(__call__)可被调用和控制,#会在创建类时,自动生成 __call__ 的方法,以备调用并触发,#如果调用 Foo(1, 2, a=2, b=4),相当于执行了 Foo.__call__(self, 1, 2, a=2, b=4) 的方法

View Code

(2)通过定制元类,控制类的实例化行为

#同理以下行为就用到了__call__的方法

classMy_mete(type):"""自定义元类,也需要继承 type"""

def __init__(self, class_name, class_bases, class_dict):if notclass_name.istitle():raise TypeError("类名的首字母必须大写,")elif "__doc__" not in class_dict or not class_dict["__doc__"].strip():raise TypeError("定义类,必须注释且注释不能为空")

super(My_mete, self).__init__(class_bases, class_bases, class_dict)def __call__(self, *args, **kwargs):"""本方法默认就有,但是,自定义了会按照自定义方法执行,

这样就可以在自定义内部增加自定义的任务与格式实现自定义功能"""

"""后台操作"""

print(self) #self = Chinese

print(args) #args = ("will", )

print(kwargs) #kwargs = {"age": 22}

#在被调用时,会触发此方法执行三件事

#1、先造出一个新对象 obj(在之前学的 object 方法有一个功能)

obj=object.__new__(self) #新建对象的语法,会提示传进去 一个类名

#2、初始化obj(触发 __init__() 方法)

self.__init__(obj, *args, **kwargs)#3、返回obj

returnobjclass Chinese(object, metaclass=My_mete):"""这样写,相当于 Chinese = My_mite(class_name, class_bases, class_dict),

也就相当于基类内必须有 __init__ 的方法"""country= "china"

def __init__(self, name, age):

self.name=name

self.age=agedeftalk(self):print("%s is talking" %self.name)

obj= Chinese("will", age=22) #同理:此操作相当于调用 Chinese.__call__(Chinese, "will", 22)

print(obj.__dict__) #验证自定义元类的实例化功能,轻松实现控制实例化过程

View Code

6、元类控制类实例化行为的应用场景与单利模式的详解

(1)单例模式(设计模式的一种):如果执行内容和结果一样,就会只有一个内存地址()属于内存优化

新的需求,不要重复调用相同内容的值创建新的内存地址造成空间浪费

#实现方式一:正常实现

classMy_SQL:"""SQL 是一个访问和处理数据库的计算机语言"""

__instance =Nonedef __init__(self):

self.host= "127.0.0.1"self.port= 3306@classmethoddefsingleton(cls):"""为了节省空间,可以判断实例化如果是同一个内容,可以覆盖

此处需要用到 装饰器@classmethod"""

if cls.__instance isNone:

obj=cls()

cls.__instance =objreturn cls.__instance

defconn(self):pass

defexecute(self):passobj1=My_SQL.singleton()

obj2=My_SQL.singleton()

obj3=My_SQL.singleton()print(obj1, obj2, obj3)#通过此方法,实现了单例行为,这样的方式是处理 普通类加装饰器实现此功能,下面用元类控制此功能

View Code

(2)元类控制实例化的应用

#方式二:元类实现此功能

classMy_mete(type):"""自定义元类,也需要继承 type"""

def __init__(self, class_name, class_bases, class_dict):if notclass_name.istitle():raise TypeError("类名的首字母必须大写,")elif "__doc__" not in class_dict or not class_dict["__doc__"].strip():raise TypeError("定义类,必须注释且注释不能为空")

super(My_mete, self).__init__(class_bases, class_bases, class_dict)

self.__instance =Nonedef __call__(self, *args, **kwargs):if not self.__instance:

obj= object.__new__(self)

self.__init__(obj)

self.__instance =objreturn self.__instance

class Mysql(object, metaclass=My_mete):"""用元类实现此功能就简单多了"""

def __init__(self):

self.host= "127.0.0.1"self.port= 3306

defconn(self):pass

defexecute(self):passobj1=Mysql()

obj2=Mysql()

obj3=Mysql()print(obj1 is obj2 is obj3) #通过自定义元类,轻松实现了重复开辟地址的行为,节省了空间

View Code

7、有关元类的小作业

作业要求:

(1)、通过自定义元类属性,把自定义类的属性全部大写

#第一题:#1、通过自定义元类属性,把自定义类的属性全部大写

classMymete(type):"""判断派生类所有属性必须都要大写"""

def __init__(self, class_name, class_bases, class_dict):if notclass_name.isupper():raise TypeError("定义类名,必须要大写")for attr inclass_dict:if not (attr.startswith("__") and attr.endswith("__")) and notattr.isupper():"""判断不是以 __开头和 __结尾的属性,都必须大写,否则抛出错误"""

raise TypeError("所有属性都必须要大写!!!")

super(Mymete, self).__init__(class_name, class_bases, class_dict)class CHINESE(object, metaclass=Mymete):"""已完成元类定制 子类所有属性全部大写"""DDD= "QQQ"

def __init__(self, name, age, sex):

self.name=name

self.age=age

self.sex=sexdefDDD(self):print(self.name)print(self.age)print(self.sex)#老师的答案:

classMymetaclass(type):def __new__(cls,name,bases,attrs):

update_attrs={}for k,v inattrs.items():if not callable(v) and not k.startswith('__'):

update_attrs[k.upper()]=velse:

update_attrs[k]=vreturn type.__new__(cls,name,bases,update_attrs)class Chinese(metaclass=Mymetaclass):

country='China'tag='Legend of the Dragon' #龙的传人

defwalk(self):print('%s is walking' %self.name)print(Chinese.__dict__)'''{'__module__': '__main__',

'COUNTRY': 'China',

'TAG': 'Legend of the Dragon',

'walk': ,

'__dict__': ,

'__weakref__': ,

'__doc__': None}'''

View Code

(2)、在元类中控制自定义的类不需要使用 __init__ 方法

①:元类帮其完成初始对象,以及初始化操作

②:要求实例化使传入参数必须是关键字参数,否则,抛出异常:TypeError:must use keyword argument

③:key 作为自定义类产生对象的属性,且所有属性变成大写

#第二题:

classMymetaclass(type):def __new__(cls,name,bases,attrs):

update_attrs={}for k,v inattrs.items():if not callable(v) and not k.startswith('__'):

update_attrs[k.upper()]=velse:

update_attrs[k]=vreturn type.__new__(cls,name,bases,update_attrs)def __call__(self, *args, **kwargs):ifargs:raise TypeError('must use keyword argument for key function')

obj= object.__new__(self) #创建对象,self为类Foo

for k,v inkwargs.items():

obj.__dict__[k.upper()]=vreturnobjclass Chinese(metaclass=Mymetaclass):

country='China'tag='Legend of the Dragon' #龙的传人

defwalk(self):print('%s is walking' %self.name)

p=Chinese(name='egon',age=18,sex='male')print(p.__dict__)

View Code

九、异常处理

1、什么是异常:异常是程序发出的错误信号,一旦程序出错且没有处理这个错误,系统就会抛出异常且程序 运行终止

异常分为三部分:

1、异常追踪信息:Traceback(会显示那里出错并定位)

2、异常的类型:ValueError

3、异常的内容及错误的说明

#print("1")#print("2")#int("ppr")

View Code

2、错误分类:

分为 2 部分:

(1)、语法错误:这个没的说,就是写程序的时候 没按照 python 约束的语法执行,就会报错,这样只能排查语法

例:if 1>2

#1不可能大于二,所以是语法书写层次的错误

View Code

(2)、逻辑错误:逻辑错误又分为以下几种

#ValueError#int("aaa")

#NameError#name

#IndexError#f = [1, 2, 3]#f[100]

#KeyError#d = {}#d["name"]

#AttributeError#class Foo:#pass#Foo.xxx

#ZeroDivisionError#1/0

#TypeError#for i in 55:#print(i)

#AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x#IOError 输入/输出异常;基本上是无法打开文件#ImportError 无法引入模块或包;基本上是路径问题或名称错误#IndentationError 语法错误(的子类) ;代码没有正确对齐#IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]#KeyError 试图访问字典里不存在的键#KeyboardInterrupt Ctrl+C被按下#NameError 使用一个还未被赋予对象的变量#SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)#TypeError 传入对象类型与要求的不符合#UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,#导致你以为正在访问它#ValueError 传入一个调用者不期望的值,即使值的类型是正确的

View Code

3、异常处理

强调一、对于错误发生的条件如果是可以预知的,此时应该用 if else 判断,去预防异常

age = 10a= input(">>>:").strip()ifa.isdigit():

a=int(a)if age >a:print("")

View Code

强调二、对于错误发生的条件如果不能预知的,此时应该用到异常处理机制, try...except

需求:如果读文件,不知 到文件 有多少行,且没有更先进的方法 和机制,就要用到抛异常了

try:

f= open("exec.txt", "r", encoding="utf-8")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="") #正常情况下是会报错的

print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")

f.close()exceptStopIteration:print("报错了")print("++++>>>1")print("++++>>>2")print("++++>>>3")#这样情况下,就不会报错了

View Code

4、异常处理之多分支:用多分支处理异常的场景:代码被检测的代码块报出的异常有多种可能性,且需要针对每一种异常类型都定制处理逻辑

#一、异常处理之多分支

try:print("++++>>>1")#name

print("++++>>2")

l[1000]print("+++++>>3")

d={}

d[name]print("++++++>>>4")exceptNameError as b:print("+++++>", b)except ImportError as c: #有点像elif 的分支结构

print("+++++++>", c)exceptKeyError as p:print("+++++>", p)#用多分支处理异常的场景:代码被检测的代码块报出的异常有多种可能性,且需要针对每一种异常类型都定制处理逻辑

View Code

5、万能异常:Exception

Exception(包含所有异常类型的异常机制)不包括语法错误,因为语法错误本不应该在程序中存在

万能异常的使用场景: 被检测的代码块报出的异常有多种可能性,且针对所有的异常,只用一种异常处理逻辑,就用 Exception

try:print("++++>>>1")

nameprint("++++>>2")

l[1000]print("+++++>>3")

d={}

d[name]print("++++++>>>4")exceptException as b:print("异常", b)print("+++++>>>")

View Code

6、二者的合并使用

try:print("++++>>>1")#name

print("++++>>2")

l[1000]print("+++++>>3")

d={}

d[name]print("++++++>>>4")exceptNameError as b:print("+++++>", b)except ImportError as c: #有点像elif 的分支结构

print("+++++++>", c)exceptKeyError as p:print("+++++>", p)except Exception as r: #在最后再用 万能异常处理不可预知的错误

print("万能异常的执行", r)print("++++++>>>>0")

View Code

7、其他结构

(1) elxe 在 try 内的作用及使用场景

try:print("++++>>>1")#name

print("++++>>2")#l[1000]

print("+++++>>3")

d={}#d[name]

print("++++++>>>4")exceptNameError as b:print("+++++>", b)except ImportError as c: #有点像elif 的分支结构

print("+++++++>", c)exceptKeyError as p:print("+++++>", p)except Exception as r: #在最后再用 万能异常处理不可预知的错误

print("万能异常的执行", r)else:print("它的 else 应用场景是:发生异常,就不会执行此处") #在被检测的代码没有发生异常时,就要执行这里的代码

print("++++++>>>>0")

View Code

(2) finally 的使用方法

finally:不管检测代码有没有发生异常都会执行

try:print("++++>>>1")

nameprint("++++>>2")

l[1000]print("+++++>>3")

d={}

d[name]print("++++++>>>4")exceptNameError as b:print("+++++>", b)except ImportError as c: #有点像elif 的分支结构

print("+++++++>", c)exceptKeyError as p:print("+++++>", p)except Exception as r: #在最后再用 万能异常处理不可预知的错误

print("万能异常的执行", r)else:print("它的 else 应用场景是:发生异常,就不会执行此处") #在被检测的代码没有发生异常时,就要执行这里的代码

finally:print("不管被检测的代码有没有异常,本方法都会正常执行!")print("++++++>>>>0")

View Code

finally 经常会应用在回收场景

try:

f= open("exec.txt", "r", encoding="utf-8")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="")print(f.__next__(), end="") #正常情况下是会报错的

print(f.__next__(), end="")#f.close() # f.close() 写在这里,可能报错之后,不能回收资源。

finally: #利用 finally 这样的写法,就可以有效的避免浪费内存资源

f.close()print("++++>>>1")print("++++>>>2")print("++++>>>3")

View Code

8、主动触发异常(raise)的应用场景

classPeople:def __init__(self, name, age, sex): #这样定义一个普通类,

if notisinstance(name, str):raise TypeError("name not is str!!!")if notisinstance(age, int):raise TypeError("age not is int!!!")

self.name=name

self.age=age

self.sex=sex#在实例化时:

p = People("will", 33, "man") #正常传值,是没问题

#主动触发异常

p2 = People(55, "11", 12) #但是这样传值(潜规则的错误),也不会报错,但是,人的名字都是字符串形式,所以就用到了主动触发异常

View Code

9、自定义异常

classMyException(BaseException):"""自定义异常时,通常要继承系统异常"""

def __init__(self, msg):

super(MyException, self).__init__()

self.msg=msgdef __str__(self):"""自定义异常,必须要有此格式,否则不能打印异常要求"""

print("" %self.msg)raise TypeError("自定义异常的处理结果!!!")

View Code

10、断言 assert

#断言(assert):代码分为 2部分的场景,且互相有依赖#第一部分:(定义部分)

info ={}

info["name"] = "will"info["age"] = 22

#第二部分:(分析与判断部分)#常用的写法:

if "name" not in info: #分析

raise KeyError("必须有 name 这个key!")if "age" not ininfo:raise KeyError("必须有 age 这个 key!!!")if info["name"] == "will" and info["age"] > 18: #判断

print("welcome! !")#断言的写法:

assert "name" in info and "age" ininfo"""断言的应用就是符合硬性条件,如果不符合,直接报错"""

if info["name"] == "will" and info["age"] > 22:print("welcome!!!!")

View Code

*#*#*#*#*#小结:异常处理常用在知道会有异常,又无法预知发生错误的具体条件,且又不能让代码系统崩溃。如果 try 和 except 用多了密密麻麻的会显得效果非常差

十、面向对象软件开发基础流程与结课作业讲解

1、简单介绍面向对象软件开发的基本流程与注意事项:软件开发其实是一整套的规范,学习的只是一小部分,一个完整的开发工程,需要明确每个阶段的任务,在保证一个阶段正确的前提下,在进行下一个阶段的工作,这种行为称之为软件工(1)、面向对象分析(OOA): 学完面向对象不要上去就是写代码,要有更深层次的分析与大局框架意识软件工程中的系统分析阶段,需要分析员与用户在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做些什么,而不是怎么去去做。

面向对象的分析要按照 面向对象的概念与方法,在对任务的分析中,从客观存在的事物和事物之间的关系,归纳出有关的对象(对象的特征和技能),

以及对象之间的联系,并将具有相同属性和行为的对象用一个类 class 来标识。

(2)、面向对象设计(OOD):根据面向对象分析阶段形成的需求模型,对每个部分 分别进行具体设计

1、首先是类的设计,类的设计可能包含多个层次(利用继承与派生的机制)。然后以这些类为基础,提出程序设计的思路和方法,包括对算法的设计。

2、在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如:伪代码或流程图)来描述

(3)、面向对象编程(OOP):根据面向对象设计的结果,选择一种计算机语言把他写成程序,可以是 python。

(4)、面向对象测试(OOT):在写好程序交给用户使用之前,必须对程序进行严格测试。

1、测试的目的是发现程序中的错误并修正它。

2、面向对象的测试:是用面向对象的方法进行测试,以类作为测试的基本单元。

(5)、面向对象的维护(OOSM)

2、作业

#-*- coding:utf-8 -*-#author:Will Loong

#本章节作业:#角色:学校,学生,课程,讲师#要求:#1、创建北京、上海2个学校#2、创建 linux、python、go 三种课程,linux在北京开、go 在上海开#3、课程包括:周期、价格、通过学校创建课程#4、通过学校创建班级,班级关联课程、讲师#5、创建学时、选择学校,关联班级#6、创建讲师角色时要关联学校#提供 2 种接口#6.1、学员视图:可以注册、交学费、选班级#6.2、讲师视图,讲师可以管理自己的班级,查看班级学员列表,修改所管理的学员成绩#6.3、管理视图,创建讲师,创建班级,创建课程#7、上面的操作产生的数据通过 pickle 序列化保存到文件里

#作业分析:#1、场景、角色、(类) ———> 属性、方法#①、课程类:课程没有具体的动作和方法,所以只需要设计属性#属性:课程名、周期、老师

#②、学生类:#属性:姓名、对应课程#方法及流程:查看可选课程、选择对应课程、查看所选课程、退出程序

#③、管理员:#属性:姓名#方法:创建课程、创建学生账号、查看所有课程信息、查看所有学生信息、查看所有学生的选课情况、退出程序

#2、站在每个角色的角度上去思考程序执行流程

importosimportsysimportpicklefrom color importfont_color#class Pea():#while True:#try:#with open("student_info", "rb") as f8:#cde = pickle.load(f8)#print(cde.name)#except EOFError:#print("aaa")#p = Pea()#print(p)

classCourse:"""定义课程类"""

def __init__(self, name, price, period, teacher):"""定义属性并初始化赋值"""self.name=name

self.price=price

self.period=period

self.teacher=teacherclassPerson:"""把打印课程提取出来,创建一个父类"""

defshow_course(self):"""查询课程"""with open("course_info", "rb") as f:

count=0whileTrue:try:

count+= 1course_obj=pickle.load(f)

font_color("%s.%s-%s-%s-%s" %(count, course_obj.name, course_obj.price, course_obj.period,

course_obj.teacher),"noting")exceptEOFError:break

classStudent(Person):"""创建学生类"""operate_lst= [("查看可选课程", "show_course"),

("选择课程", "select_course"),

("查看已选课程!", "check_selected_course"),

("退出", "exit")]def __init__(self, name):"""一个学生不一定只有一门课程,所以在定义学生课程时,格式化一个列表"""self.name=name

self.course_name=[]defselect_course(self):

self.show_course()

num= int(input("Number >>>:").strip())

count= 1with open("course_info", "rb") as f:whileTrue:try:

course_obj=pickle.load(f)if count ==num:

self.course_name.append(course_obj)print(len(self.course_name))

font_color("You chose the %s course !!!" % course_obj.name, "true")breakcount+= 1

exceptEOFError:

font_color("there is not course!!!", "error")break

defcheck_selected_course(self):"""查看已选课程"""

print(self.course_name)for cou inself.course_name:print(cou.name, cou.teacher)defexit(self):"""把写入的文件创建在最后,如果修改完了退出,再写入文件"""with open("student_info", "rb") as f1, open("student_info_back", "wb") as f2:"""在内存打开2个文件,f1, f2"""

whileTrue:try:

student_f=pickle.load(f1)if student_f.name ==self.name:"""如果相等,就把修改后的文件存入 f2 文件"""pickle.dump(self, f2)else:"""否则,原内容保持不变"""pickle.dump(student_f, f2)exceptEOFError:breakos.remove("student_info")

os.rename("student_info_back", "student_info")

exit()

@staticmethoddefinit(name):"""返回一个学生对象,在student内"""with open("student_info", "rb") as f:whileTrue:try:

stu_obj= pickle.load(f, encoding="utf-8")if stu_obj.name ==name:returnstu_objexceptEOFError:

font_color("学生账号不存在!!!", "error")break

classManager(Person):"""创建管理员"""operate_lst= [("创建课程", "create_course"),

("创建学生账号", "create_student"),

("查看所有课程", "show_course"),

("查看所有学生", "show_student"),

("查看所有学生选课情况", "show_student_course"),

("退出", "exit")]def __init__(self, name):

self.name=namedefcreate_course(self):"""创建课程"""name= input("course name:").strip()

price= input("course price:").strip()

period= input("course period:").strip()

teacher= input("course teacher:").strip()

course_obj=Course(name, price, period, teacher)

with open("course_info", "ab") as f:

pickle.dump(course_obj, f)

font_color("%s 课程创建完毕!" % course_obj.name, "yes")defcreate_student(self):"""创建学生账号:

1、用户名和密码以及身份记录到 user_info 文件内

2、将学生对象存进student 文件"""stu_name= input("student name:")if isinstance(stu_name.strip(), str) is notNone:

stu_pwd= input("student password:").strip()if isinstance(stu_pwd.strip(), (str or int)) is notNone:

stu_pwd2= input("student password:").strip()if stu_pwd2 ==stu_pwd:

stu_auth= "%s|%s|Student" % (stu_name, stu_pwd2) + "\n"stu_obj=Student(stu_name)

with open("user_info.txt", "a", encoding="utf-8") as f:

f.write(stu_auth)

with open("student_info", "ab") as f:

pickle.dump(stu_obj, f)

font_color("%s学生账号创建成功!!!" % stu_obj.name, "yes")else:

font_color("2次密码输入不一致!!!", "error")else:

font_color("学生账号必须是 str 格式!!!", "error")defshow_student(self):"""查询学生"""with open("student_info", "rb") as f:

count=0whileTrue:try:

count+= 1student_obj= pickle.load(f, encoding="utf-8")

font_color("%s.%s" % (count, student_obj.name), "yes")exceptEOFError:breakfont_color("-----------end------------", "false")defshow_student_course(self):

with open("student_info", "rb") as f:whileTrue:try:

student_obj=pickle.load(f)

course_list= [cor for cor instudent_obj.courses]

font_color("%s.opt course %s" % (student_obj.name, "|".join(course_list)), "false")exceptEOFError:break@classmethoddefinit(cls, name):returncls(name)defexit(self):

exit()#学生角度:登录就可以选课了#已经有账号,已经有课程

#管理员:登录就可以完成以下操作:#学生账号由管理员创建#学生课程由管理员创建

#应该先创建管理员角色更合适着手开发#登录需求:#必须自动识别身份(存在文件内的信息:账号、密码、身份)

deflogin():

user_name= input("username:").strip()

passwod= input("password:").strip()

with open("user_info", "r", encoding="utf-8") as f:for line inf:

user, pwd, identify= line.strip().split("|")if user == user_name and passwod ==pwd:return {"state": True, "name": user_name, "id": identify}else:return {"state": False, "name": user_name}

log_res=login()if log_res["state"]:

font_color("登录成功!!!", "yes")if hasattr(sys.modules[__name__], log_res["id"]):

cls= getattr(sys.modules[__name__], log_res["id"])

obj= cls(log_res["name"])whileTrue:for idx, nature in enumerate(cls.operate_lst, 1):

font_color("%s.%s" % (idx, nature[0]), "noting")

inp= input("please>>>:").strip()if inp.isdigit() and len(cls.operate_lst) >=int(inp):

func_str= cls.operate_lst[int(inp) - 1][1]ifhasattr(obj, func_str):

getattr(obj, func_str)()else:

font_color("No such option!!!", "error")else:

font_color("登录失败!!!", "error")

View Code

*****老师讲解

#-*- coding: utf-8 -*-#@Time : 2018/8/31 10:59#@Author : 骑士计划#@Email : customer@luffycity.com#@File : 5.作业讲解.py#@Software: PyCharm

importosimportsysimportpickle

student_info= 'student_info'course_info= 'course_info'userinfo= 'userinfo'

classBase:def __str__(self):returnself.nameclassCourse(Base):def __init__(self,name,price,period,teacher):

self.name=name

self.price=price

self.period=period

self.teacher=teacherdef __repr__(self):return ' '.join([self.name, self.price, self.period, self.teacher])classPerson:

@staticmethoddefget_from_pickle(path):

with open(path,'rb') as f:whileTrue:try:

stu_obj=pickle.load(f)yieldstu_objexceptEOFError:break

defshow_courses(self):for count,course in enumerate(self.get_from_pickle(course_info),1):print(count,repr(course))defdump_obj(self,path,obj):

with open(path,'ab') as f:

pickle.dump(obj,f)classStudent(Person,Base):

operate_lst=[

('查看所有课程', 'show_courses'),

('选择课程', 'select_course'),

('查看已选课程', 'check_selected_course'),

('退出', 'exit')]def __init__(self,name):

self.name=name

self.courses=[]def __repr__(self):#course_name = [course.name for course in self.courses]

course_name = [str(course) for course inself.courses]return '%s %s'%(self.name,'所选课程%s' % '|'.join(course_name))defselect_course(self):

self.show_courses()

num= int(input('num >>>'))for count,course in enumerate(self.get_from_pickle(course_info),1):if count ==num:

self.courses.append(course)print('您选择了%s课程' %(course))break

else:print('没有您要找的课程')defcheck_selected_course(self):for course inself.courses:print(course.name,course.teacher)defexit(self):

with open(student_info+'_bak', 'wb') as f2:for stu inself.get_from_pickle(student_info):if stu.name == self.name: #如果从原文件找到了学生对象和我当前的对象是一个名字,就认为是一个人

pickle.dump(self, f2) #应该把现在新的学生对象写到文件中

else:

pickle.dump(stu, f2)#反之,应该原封不动的把学生对象写回f2

os.remove(student_info)

os.rename(student_info+'_bak',student_info)

exit()

@classmethoddefinit(cls,name):for stu incls.get_from_pickle(student_info):if stu.name ==name:returnstuelse:print('没有这个学生')classManager(Person):

operate_lst= [('创建课程','create_course'),

('创建学生','create_student'),

('查看所有课程','show_courses'),

('查看所有学生','show_students'),

('查看所有学生的选课情况','show_student_course'),

('退出','exit')]def __init__(self,name):

self.name=namedefcreate_course(self):

name= input('course name :')

price= input('course price :')

period= input('course period :')

teacher= input('course teacher :')

course_obj=Course(name,price,period,teacher)

self.dump_obj(course_info, course_obj)print('%s课程创建成功'%course_obj.name)defcreate_student(self):#用户名和密码记录到userinfo文件,将学生对象存储在student_info文件

stu_name =input('student name :')

stu_pwd=input('student password :')

stu_auth= '%s|%s|Student\n'%(stu_name,stu_pwd)

stu_obj=Student(stu_name)

with open(userinfo,'a',encoding='utf-8') as f:

f.write(stu_auth)

self.dump_obj(student_info, stu_obj)print('%s学生创建成功'%stu_obj.name)defshow_students(self):for count,stu in enumerate(self.get_from_pickle(student_info),1):print(count,stu)defshow_student_course(self):for stu inself.get_from_pickle(student_info):print(repr(stu))defexit(self):

exit()

@classmethoddefinit(cls,name):return cls(name) #管理员的对象

deflogin():

name= input('username :')

pawd= input('password :')

with open(userinfo,encoding='utf-8') as f:for line inf:

usr,pwd,identify= line.strip().split('|')if usr == name and pawd ==pwd:return {'result':True,'name':name,'id':identify}else:return {'result':False,'name':name}

ret=login()if ret['result']:print('\033[1;32;40m登录成功\033[0m')if hasattr(sys.modules[__name__],ret['id']):

cls= getattr(sys.modules[__name__],ret['id'])

obj= cls.init(ret['name']) #实例化

whileTrue:for id,item in enumerate(cls.operate_lst,1):print(id,item[0])

func_str= cls.operate_lst[int(input('>>>')) - 1][1]print(func_str)ifhasattr(obj,func_str):

getattr(obj,func_str)()else:print('登录失败')

View Code

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值