Python之面向对象详解(一篇足矣)

          

目录

一、初阶面向对象

1. 初识面向对象

1.1 对象和self

1.2 常见成员

1.3 应用示例

将数据封装到一个对象,便于以后使用。

将数据封装到对象中,在方法中对原始数据进行加工处理。

根据类创建多个对象,在方法中对对象中的数据进行修改。

2. 三大特性

2.1 封装

2.2 继承

2.3 多态

3. 扩展:再看数据类型

二、进阶面向对象

1.成员

1.1 变量

1.2 方法

1.3 属性

2.成员修饰符

3.对象嵌套

4.特殊成员

三、补充

1. 继承【补充】

1.1 mro和c3算法

1.2 py2和py3区别(了解)

2. 内置函数补充

3.异常处理

3.1 异常细分

3.2 自定义异常和抛出异常

3.3 特殊的finally


        许久未更新,我们将进入系列课程第3个模块的的学习,此模块包含如下三大部分知识:面向对象,Python中支持两种编程方式来写代码,分别是:函数式编程面向对象式编程

一、初阶面向对象

  • 函数式,推荐初学者使用。理由:上手快且逻辑简单清晰。

    # 定义函数,在函数中实现功能
    def func():print("一个NB的功能")# 执行函数
    func()
    
  • 面向对象,推荐有一些代码经验后使用。理由:面向对象的思想需要有一定的项目积累之后(写多了并且看的多)才能真正理解其精髓,基于面向对象可以编写出扩展性更强的代码(在一定程序上也可以简化代码)。

    # 定义类
    class Foo(object):# 在类中定义方法def func(self):print("一个NB的功能")# 实例化类的对象
    obj = Foo()
    # 执行类中的方法
    obj.func()
    

补充:除此之外,在后面我们还会介绍到网络编程和并发编程。

  • 网络编程,学习网络知识后,可以让我们的程序通过网络来进行数据交互和传输 并 掌握其本质。

例如:下载10个抖音视频,每个需要2分钟。
- 按照以前的思路,逐一下载就需要20分钟。
- 按照并发的思路,创建10个线程/进程来实现,大概需要2分钟就可以完成。
  • 并发编程,一个程序想要执行的速度更快是必须要掌握并发编程的相关知识。

        Python支持两种编程方式(其他很多语言只支持一种),所以初学者在刚开始学习往往不知道应如何选择,并且行业内对于 函数式编程 vs 面向对象编程 之间谁更好的讨论也是难分胜负,其实在开发中无论要实现什么样的功能,两种编程模式都能实现,那种让我们能更好实现就选择谁?不必非纠结于那种方式更好,编程语言支持工具,最重要的是把功能实现。初学者在选择编程方式时候,可以遵循如下规则:掌握相关知识点 并且 读懂源码 以及 编写简单的基于面向对象的程序。

1. 初识面向对象

想要通过面向对象去实现某个或某些功能时需要2步:

  • 定义类,在类中定义方法,在方法中去实现具体的功能。
  • 实例化类并的个一个对象,通过对象去调用并执行方法。

注意:

  1. 类名称首字母大写以及驼峰式命名;
  2. py3之后默认类都继承object;
  3. 在类种编写的函数称为方法;
  4. 每个方法的第一个参数是self;
  5. 类中可以定义多个方法。

        你会发现,用面向对象编程写的类有点像归类的意思:将某些相似的函数划分到一个类中。但这种编写方式让人感觉有些鸡肋,直接用 函数 写多好呀(函数+模块也可实现)。对吧?别着急,记者往下看。

1.1 对象和self

        在每个类中都可以定义个特殊的:__init__ 初始化方法 ,在实例化类创建对象时自动执行,即:对象=类名()

class Message:  #定义类#定义类方法def __init__(self, content):self.data = contentdef send_email(self, email):  data = "给{}发邮件,内容是:{}".format(email, self.data)print(data)def send_wechat(self, vid):data = "给{}发微信,内容是:{}".format(vid, self.data)print(data)# 对象 = 类名() # 自动执行类中的 __init__ 方法。
# 1. 根据类型创建一个对象,内存的一块区域 。
# 2. 执行__init__方法,模块会将创建的那块区域的内存地址当self参数传递进去。往区域中(data="注册成功")msg_object = Message("注册成功")  #实例化对象(根据类创建一个对象)
#调用类方法
msg_object.send_email("xiaofeng@live.com") # 给xiaofeng@live.com发邮件,内容是:注册成功
msg_object.send_wechat("小峰") # 给小峰发微信,内容是:注册成功

通过上述的示例,你会发现:

  • 对象,让我们可以在它的内部先封装一部分数据,以后想要使用时,再去里面获取。对象其实就是基于类实例化出来”一块内存“,默认里面没有数据;经过类的 __init__方法,可以在内存中初始化一些数据。
  • self,类中的方法需要由这个类的对象来触发并执行( 对象.方法名 ),且在执行时会自动将对象当做参数传递给self,以供方法中获取对象中已封装的值。self,本质上就是一个参数。这个参数是Python内部会提供,其实本质上就是调用当前方法的那个对象。
  • 面向对象的思想:将一些数据封装到对象中,在执行方法时,再去对象中获取。函数式的思想:函数内部需要的数据均通过参数的形式传递

注意:除了self默认参数以外,方法中的参数的定义和执行与函数是相同。当然,根据类也可以创建多个对象并执行其中的方法。

1.2 常见成员

在编写面向对象相关代码时,最常见成员有:

  • 实例变量,属于对象,只能通过对象调用。
  • 绑定方法,属于类,通过对象调用 或 通过类调用

注意:还有很多其他的成员,后续再来逐一介绍。

类和对象总结:

  • 仅做数据封装。
  • 封装数据 + 方法再对数据进行加工处理。
  • 创建同一类的数据且同类数据可以具有相同的功能(方法)。

class Person:def __init__(self, n1, n2):# 实例变量self.name = n1self.age = n2# 绑定方法def show(self):msg = "我叫{},今年{}岁。".format(self.name, self.age)print(msg)def all_message(self):msg = "我是{}人,我叫{},今年{}岁。".format(Person.country, self.name, self.age)print(msg)def total_message(self):msg = "我是{}人,我叫{},今年{}岁。".format(self.country, self.name, self.age)print(msg)
# 执行绑定方法
p1 = Person("小峰",20)  #初始化,实例化了Person类的对象叫p1
p1.show()
# 或
# p1 = Person("小峰",20)
# Person.show(p1)
p1 = Person("root",99) 
p1.show()

1.3 应用示例

  1. 将数据封装到一个对象,便于以后使用。

    class UserInfo:def __init__(self, name, pwd,age):self.name = nameself.password = pwdself.age = agedef run():user_object_list = []# 用户注册while True:user = input("用户名:")if user.upper() == "Q":breakpwd = input("密码")# user_object对象中有:name/passworduser_object = UserInfo(user, pwd,19)# user_dict = {"name":user,"password":pwd}user_object_list.append(user_object)# user_object_list.append(user_dict)# 展示用户信息for obj in user_object_list:print(obj.name, obj.password)总结:- 数据封装到对象,以后再去获取。- 规范数据(约束)
    

    注意:用字典也可以实现做封装,只不过字典在操作值时还需要自己写key,面向对象只需要 . 即可获取对象中封装的数据。

  2. 将数据封装到对象中,在方法中对原始数据进行加工处理。

    user_list = ["用户-{}".format(i) for i in range(1,3000)]
    # 分页显示,每页显示10条
    while True:page = int(input("请输入页码:"))start_index = (page - 1) * 10end_index = page * 10page_data_list = user_list[start_index:end_index]for item in page_data_list:print(item)
    
    class Pagination:def __init__(self, current_page, per_page_num=10):self.per_page_num = per_page_numif not current_page.isdecimal():self.current_page = 1returncurrent_page = int(current_page)if current_page < 1:self.current_page = 1returnself.current_page = current_pagedef start(self):return (self.current_page - 1) * self.per_page_numdef end(self):return self.current_page * self.per_page_numuser_list = ["用户-{}".format(i) for i in range(1, 3000)]# 分页显示,每页显示10条
    while True:page = input("请输入页码:")# page,当前访问的页码# 10,每页显示10条数据# 内部执行Pagination类的init方法。pg_object = Pagination(page, 20)page_data_list = user_list[ pg_object.start() : pg_object.end() ]for item in page_data_list:print(item)
  3. 根据类创建多个对象,在方法中对对象中的数据进行修改。

    class Police:"""警察"""def __init__(self, name, role):self.name = nameself.role = roleif role == "队员":self.hit_points = 200else:self.hit_points = 500def show_status(self):""" 查看警察状态 """message = "警察{}的生命值为:{}".format(self.name, self.hit_points)print(message)def bomb(self, terrorist_list):""" 投炸弹,炸掉恐怖分子 """for terrorist in terrorist_list:terrorist.blood -= 200terrorist.show_status()class Terrorist:""" 恐怖分子 """def __init__(self, name, blood=300):self.name = nameself.blood = blooddef shoot(self, police_object):""" 开枪射击某个警察 """police_object.hit_points -= 5police_object.show_status()self.blood -= 2def strafe(self, police_object_list):""" 扫射某些警察 """for police_object in police_object_list:police_object.hit_points -= 8police_object.show_status()def show_status(self):""" 查看恐怖分子状态 """message = "恐怖分子{}的血量值为:{}".format(self.name, self.blood)print(message)def run():# 1.创建3个警察p1 = Police("小峰", "队员")p2 = Police("苑昊", "队员")p3 = Police("于超", "队长")# 2.创建2个匪徒t1 = Terrorist("alex")t2 = Terrorist("eric")# alex匪徒射击于超警察t1.shoot(p3)# alex扫射t1.strafe([p1, p2, p3])# eric射击苑昊t2.shoot(p2)# 小峰炸了那群匪徒王八蛋p1.bomb([t1, t2])# 小峰又炸了一次alexp1.bomb([t1])
    if __name__ == '__main__':run()
    

    2. 三大特性

    面向对象编程在很多语言中都存在,这种编程方式有三大特性:封装、继承、多态。

    2.1 封装

    封装主要体现在两个方面:

    • 同一类方法封装到了一个类中,例如上述示例中:匪徒的相关方法都写在Terrorist类中;警察的相关方法都写在Police类中。
    • 数据封装到了对象中,在实例化一个对象时,可以通过__init__初始化方法在对象中封装一些数据,便于以后使用。

    2.2 继承

    传统的理念中有:儿子可以继承父亲的财产。在面向对象中也有这样的理念,即:子类可以继承父类中的方法和类变量(不是拷贝一份,父类的还是属于父类,子类可以继承而已)。

    class Base:def func(self):print("Base.func")class Son(Base):def show(self):print("Son.show")s1 = Son()
    s1.show()
    s1.func() # 优先在自己的类中找,自己没有才去父类。对象到底是谁?优先就会先去谁里面找。s2 = Base()
    s2.func()
    

    小结:

    • 执行对象.方法时,优先去当前对象所关联的类中找,没有的话才去她的父类中查找。
    • Python支持多继承:先继承左边、再继承右边的。
    • 当前self到底是谁?去self对应的那个类中去获取成员,没有就按照继承关系向上查找 。

    2.3 多态

    多态,按字面翻译其实就是多种形态。其他编程语言中,是不允许这样类编写的,例如:Java

    class Cat{  public void eat() {  System.out.println("吃鱼");  }  
    }
    class Dog {  public void eat() {  System.out.println("吃骨头");  }  public void work() {  System.out.println("看家");  }  
    }public class Test {public static void main(String[] args) {obj1 = Cat()obj2 = Cat()show(obj1)show(obj2)obj3 = Dog()show(obj3)}  public static void show(Cat a)  {a.eat()}  
    } 
    

     java中正确的写法:

    abstract class Animal {  abstract void eat();  
    }  
    class Cat extends Animal {  public void eat() {  System.out.println("吃鱼");  }  
    }
    class Dog extends Animal {  public void eat() {  System.out.println("吃骨头");  }  public void work() {  System.out.println("看家");  }  
    }public class Test {public static void main(String[] args) {obj1 = Cat()show(obj1)obj2 = Dog()show(obj2)}  public static void show(Animal a)  {a.eat()}  
    } 
    

    Tips

            在java或其他语言中的多态是基于:接口 或 抽象类和抽象方法 来实现,让数据可以以多种形态存在。在Python中则不一样,由于Python对数据类型没有任何限制,所以他天生支持多态

    class Email(object):def send(self):print("发邮件")class Message(object):def send(self):print("发短信")def func(arg):v1 = arg.send()  # 浅拷贝v1 = Email()
    func(v1)
    v2 = Message()
    func(v2)
    

            在程序设计中,鸭子类型(duck typing)是动态类型的一种风格。在鸭子类型中,关注点在于对象的行为,能作什么;而不是关注对象所属的类型。例如:一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟可以被称为鸭子。

    三大特性的小结:

    • 封装,将方法封装到类中 或 将数据封装到对象中,便于以后使用。

    • 继承,将类中的公共的方法提取到基类中去实现。

    • 多态,Python默认支持多态(这种方式称之为鸭子类型),如上面的代码即可。

    3. 扩展:再看数据类型

            在初步了解面向对象之后,再来看看我们之前学习的:str、list、dict等数据类型,他们其实都一个类,根据类可以创建不同类的对象。

            补充:在Python3中编写类时,默认都会继承object(即使不写也会自动继承)。在Python2是不同的:继承object,新式类;不继承object,经典类。

    二、进阶面向对象

    1.成员

    1.1 变量

    • 实例变量,属于对象,每个对象中各自维护自己的数据
    • 类变量,属于类,可以被所有对象共享,一般用于给对象提供公共数据(类似于全局变量)
    class Person(object):country = "中国"  #类变量def __init__(self, name, age):self.name = nameself.age = age  #实例变量

    提示:当把每个对象中都存在的相同的实例变量时,可以选择把它放在类变量中,这样就可以避免对象中维护多个相同数据。

    • 注意读和写的区别。
    class Person(object):country = "中国"def __init__(self, name, age):self.name = nameself.age = age
    print(Person.country) # 中国p1 = Person("小峰",20)
    print(p1.name) # 小峰
    print(p1.age) # 20
    print(p1.country) # 中国p1.name = "root"     # 在对象p1中讲name重置为root
    p1.num = 19          # 在对象p1中新增实例变量 num=19
    p1.country = "china" # 在对象p1中新增实例变量 country="china"print(p1.country)   # china
    print(Person.country) # 中国
    Person.country="china"
    print(Person.country) #china
    
    • 继承关系中的读写
    class Base(object):country = "中国"
    class Person(Base):def __init__(self, name, age):self.name = nameself.age = age
    # 读
    print(Base.country) # 中国
    print(Person.country) # 中国obj = Person("小峰",19)
    print(obj.country) # 中国# 写
    Base.country = "china"
    Person.country = "泰国"
    obj.country = "日本"
    print(obj.country)  #日本
    print(Person.country) #泰国
    print(Base.country)  #china
    
    • 例题:
    class Parent(object):x = 1
    class Child1(Parent):pass
    class Child2(Parent):passprint(Parent.x, Child1.x, Child2.x) # 1 1 1
    Child1.x = 2
    print(Parent.x, Child1.x, Child2.x) # 1 2 1
    Parent.x = 3
    print(Parent.x, Child1.x, Child2.x) # 3 2 3
    

    1.2 方法

    • 绑定方法,默认有一个self参数,由对象进行调用(此时self就等于调用方法的这个对象)【对象&类均可调用
    • 类方法,默认有一个cls参数,用类或对象都可以调用(此时cls就等于调用方法的这个类)【对象&类均可调用
    • 静态方法无默认参数,用类和对象都可以调用。【对象&类均可调用
    class Foo(object):def __init__(self, name, age):#初始化方法self.name = nameself.age = agedef f1(self):print("绑定方法", self.name)@classmethoddef f2(cls):print("类方法", cls)@staticmethoddef f3():print("静态方法")# 绑定方法(对象)
    obj = Foo("小峰", 20)
    obj.f1()  
    Foo.f1(obj)# 类方法
    Foo.f2()  # cls就是当前调用这个方法的类。(类)
    obj.f2()  # cls就是当前调用这个方法的对象的类。# 静态方法
    Foo.f3()  # 类执行执行方法(类)
    obj.f3()  # 对象执行执行方法

    Tips:      在Python中比较灵活,方法都可以通过对象和类进行调用而在java、c#等语言中,绑定方法只能由对象调用;类方法或静态方法只能由类调用

    思考题:在类中 @classmethod 和 @staticmethod 的作用是什么?

    1.3 属性

            属性其实就是由绑定方法 + 特殊装饰器 组合创造出来的,属性的出现让我们以后在调用方法时可以不加括号,例如:

    class Foo(object):def __init__(self, name):self.name = namedef f1(self):return 123@propertydef f2(self):return 123obj = Foo("小峰")v1 = obj.f1()
    print(v1)v2 = obj.f2 #v2 = obj.f2()
    print(v2)
    

            其实,除了咱们写的示例意外,在很多模块和框架的源码中也有porperty的身影,例如:requests模块。

    import requests# 内部下载视频,并将下载好的数据分装到Response对象中。
    res = requests.get(url="https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0200f240000buuer5aa4tij4gv6ajqg",headers={"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 FS"}
    )# 去对象中获取text,其实需要读取原始文本字节并转换为字符串
    res.text
    

    关于属性的编写有两种方式:

    • 方式一,基于装饰器

      class C(object):def __init__(self):self.value=0@propertydef x(self):return self.value@x.setterdef x(self, value):self.value = value@x.deleterdef x(self):self.value=0obj = C()
      obj.x
      obj.x = 123
      print(obj.x)  #123
      del obj.x
      print(obj.x)  #0
      
    • 方式二,基于定义变量

      class C(object):def __init__(self):self.value=0def getx(self):return self.valuedef setx(self, value):self.value = valuedef delx(self):self.value=0x = property(getx, setx, delx, "I'm the 'x' property.")obj = C()
      obj.x
      obj.x = 123
      print(obj.x)  #123
      del obj.x
      print(obj.x)  #0

            最后,对属性进行一个补充:由于属性和实例变量的调用方式相同,所以在编写时需要注意——属性名称 不要 实例变量 重名。一旦重名,可能就会有报错。如果真的想要在名称上创建一些关系,可以让实例变量加上一个下划线

    class Foo(object):def __init__(self, name, age):self._name = nameself.age = age@propertydef name(self):return "{}-{}".format(self._name, self.age)obj = Foo("小峰", 123)
    print(obj._name)
    print(obj.name)
    

    2.成员修饰符

    Python中成员的修饰符就是指的是公有、私有

    • 公有,任何地方都可以调用这个成员
    • 私有,只有在类的内部才可以调用该成员成员是以两个下划线开头,则表示该成员为私有)。

    示例:

    class Foo(object):def __init__(self, name, age):self.__name = nameself.age = agedef get_data(self):return self.__namedef get_age(self):return self.agedef get_age1(self):print("公有的get_age1")def __get_data1(self):print("私有的__get_data1方法")def proxy(self):print("公有的proxy")self.__get_data1()#成员的访问
    obj = Foo("小峰", 123)
    # 公有成员
    print(obj.age)
    v1 = obj.get_age()
    print(v1)
    # 私有成员
    # print(obj.__name) # 错误,由于是私有成员,只能在类中进行使用。
    v2 = obj.get_data()
    print(v2)#方法的访问
    o1 = Foo("xiaofeng",10)
    o1.get_age1()
    # o1.__get_data1()   #私有方法只能在类内部进行访问
    o1.proxy()

    示例:

    class Foo(object):def __init__(self, name, age):self.__name = nameself.age = age@propertydef __get_age(self):print("公有的get_age")return self.age@propertydef proxy(self):print("公有的proxy")return self.__nameobj = Foo("xiaowu", 18)
    v1 = obj.proxy
    print(v1)
    # v2=obj.__get_age   #私有属性,只能在类内部进行访问
    # print(v2)

    特别提醒:父类中的私有成员,子类无法继承

    class Base(object):def __data(self):print("base.__data")def num(self):print("base.num")class Foo(Base):def func1(self):self.num()def func2(self):self.__data()  # 不允许执行父类中的私有方法obj = Foo()
    obj.func1()
    #obj.func2() #会报错,子类不继承父类的私有成员(变量、方法、属性)
    

    Tips:

            写在最后,按理说私有成员是无法被外部调用,但如果用一些特殊的语法也可以(Flask源码中有这种写法,大家写代码不推荐这样写   【_类名__变量名/方法名/属性名】)。

            成员是否可以作为独立的功能暴露给外部,让外部调用并使用。可以——公有使用;不可以,内部其他方法做辅助,私有使用。

    class Foo(object):def __init__(self):self.__num = 123self.age = 19def __msg(self):print(1234)obj = Foo()
    #访问私有变量
    print(obj.age)
    #print(obj.__num())  #会报错
    print(obj._Foo__num)
    #访问私有方法
    # print(obj.__msg())  #会报错
    obj._Foo__msg()

      3.对象嵌套

      在基于面向对象进行编程时,对象之间可以存在各种各样的关系,例如:组合、关联、依赖等(Java中的称呼),用大白话来说就是各种嵌套

      下面我们就用示例来学习常见的嵌套的情景:

      示例一:

      class Student(object):""" 学生类 """def __init__(self, name, age):self.name = nameself.age = agedef message(self):data = "我是一名学生,我叫:{},我今年{}岁".format(self.name, self.age)print(data)class Classes(object):""" 班级类 """def __init__(self, title):self.title = titleself.student_list = []def add_student(self, stu_object):self.student_list.append(stu_object)def add_students(self, stu_object_list):for stu in stu_object_list:self.add_student(stu)
      s1 = Student("小峰", 19)
      s2 = Student("小张", 19)
      s3 = Student("日天", 19)c1 = Classes("三年二班")
      c1.add_student(s1)
      c1.add_students([s2, s3])print(c1.title)
      print(c1.student_list) #这样打印出来的只是学生对应对象的存储地址
      for i in range(len(c1.student_list)):  #正确的遍历班级成员列表,并打印出学生的相关信息print(c1.student_list[i].name, c1.student_list[i].age)
      

      示例二:

      class Student(object):""" 学生类 """def __init__(self, name, age, class_object):self.name = nameself.age = ageself.class_object = class_objectdef message(self):data = "我是一名{}班的学生,我叫:{},我今年{}岁".format(self.class_object.title, self.name, self.age)print(data)class Classes(object):""" 班级类 """def __init__(self, title, school_object):self.title = titleself.school_object = school_objectclass School(object):""" 学校类 """def __init__(self, name):self.name = name
      #实例化学校类
      s1 = School("北京校区")
      s2 = School("上海校区")
      #实例化班级类
      c1 = Classes("Python全栈", s1)
      c2 = Classes("Linux云计算", s2)
      user_object_list = [Student("小峰", 19, c1),Student("小王", 19, c1),Student("小白", 19, c2)
      ]
      for obj in user_object_list:print(obj.name, obj.class_object.title, obj.class_object.school_object.name)

      4.特殊成员

              在Python的类中存在一些特殊的方法,这些方法都是 __方法__ 格式,这种方法在内部均有特殊的含义,接下来我们来讲一些常见的特殊成员:

      • __init__,初始化方法

        class Foo(object):def __init__(self, name):self.name = name
        obj = Foo("小峰")
        
      • __new__,构造方法

        class Foo(object):def __init__(self, name):print("第二步:初始化对象,在空对象中创建数据")self.name = namedef __new__(cls, *args, **kwargs):print("第一步:先创建空对象并返回")return object.__new__(cls)obj = Foo("小峰")
        
      • __call__,对象()的时候自动执行

        class Foo(object):def __call__(self, *args, **kwargs):print("执行call方法")obj = Foo()  
        obj()  # 当遇见 对象名() 的时候就会自动执行call方法
        
      • __str__,该方法中必须返回一个字符串

        class Foo(object):def __str__(self):return "必须返回字符串"obj = Foo("xiaowang")
        #下面两个语句打印的结果是一样的,第一个是对第二个语句内部做了优化而已
        print(obj)
        print(str(obj))
        
      • __dict__,字典对象的支持【对象[''] 对象[]=xxxxdel 对象[]】可以和下面的三种方法搭配使用

        class Foo(object):def __init__(self, name, age):self.name = nameself.age = ageobj = Foo("小峰",19)
        print(obj.__dict__)
        
      • __getitem____setitem____delitem__

        class Foo(object):def __setitem__(self, key, value):self.value = value# print(key, value)def __getitem__(self, value):return self.valuedef __delitem__(self,key):self.value = Nonereturn keyobj = Foo()
        obj["feng"]=19
        print(obj.__dict__)
        print(obj["feng"])
        obj['feng'] = 21
        print(obj["feng"])
        del obj["feng"]
        print(obj["feng"])
        
      • __enter____exit__,上下文管理和文件操作那块所讲的类似

        class Foo(object):def __enter__(self):print("进入了")return 666def __exit__(self, exc_type, exc_val, exc_tb):print("出去了")obj = Foo()
        with obj as data:print(data)
        
      • __add__ 等操作函数

        class Foo(object):def __init__(self, name):self.name = namedef __add__(self, other):return "{}-{}".format(self.name, other.name)v1 = Foo("I")
        v2 = Foo("Love")
        # 对象+值,内部会去执行 对象.__add__方法,并将+后面的值当做参数传递过去。
        v3 = v1 + v2
        print(v3)
        v4 = Foo("you")v5=Foo(v3)+v4
        print(v5)
      • __iter__迭代器

      迭代器类型的定义:
                  1.当类中定义了 __iter__ 和 __next__ 两个方法。
                  2.__iter__ 方法需要返回对象本身,即:self
                  3. __next__ 方法,返回下一个数据,如果没有数据了,则需要抛出一个StopIteration的异常。

              迭代器对象支持通过next取值,如果取值结束则自动抛出StopIteration。for循环内部在循环时,先执行__iter__方法,获取一个迭代器对象,然后不断执行的next取值(有异常StopIteration则终止循环)。
          
      官方文档:https://docs.python.org/3/library/stdtypes.html#iterator-types

      class IT(object):def __init__(self):self.counter = 0def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 3:raise StopIteration()return self.counter
      # 根据类实例化创建一个迭代器对象:
      obj1 = IT()
      v1 = next(obj1)  # obj1.__next__()
      print(v1)
      v2 = next(obj1)
      print(v2)
      v3 = next(obj1) # 抛出异常,异常会在后面的内容中讲到
      print(v3)############################
      obj2 = IT()
      for item in obj2:  # 首先会执行迭代器对象的__iter__方法并获取返回值,一直去反复的执行 next(对象) print(item)
      
      • 生成器,创建生成器对象(内部是根据生成器类generator创建的对象),生成器类的内部也声明了:__iter__、__next__ 方法。如果按照迭代器的规定来看,其实生成器类也是一种特殊的迭代器类(生成器也是一个中特殊的迭代器)。

      def func():yield 1yield 2obj2 = func()
      for item in obj2:print(item)
      • 可迭代对象。如果一个类中有__iter__方法且返回一个迭代器对象 ,则我们称以这个类创建的对象为可迭代对象可迭代对象,是可以使用for来进行循环,在循环的内部其实是先执行 __iter__ 方法,获取其迭代器对象,然后再在内部执行这个迭代器对象的next功能,逐步取值。

      class Foo1(object):def __iter__(self):return 迭代器对象(生成器对象)
      obj1 = Foo1() # obj是 可迭代对象。
      for item in obj1:passclass Foo2(object):def __iter__(self):yield 1yield 2
      obj2 = Foo2()
      for item in obj2:print(item)
      
      class IT(object):def __init__(self):self.counter = 0def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == 3:raise StopIteration()return self.counter     # 可迭代对象
      class  Foo(object):def __iter__(self):return IT()
      obj = Foo()         
      for item in obj:    # 循环可迭代对象时,内部先执行obj.__iter__并获取迭代器对象;不断地执行迭代器对象的next方法。print(item)

      基于可迭代对象和迭代器实现:自定义range 。

      class IterRange(object):def __init__(self, num):self.num = numself.counter = -1def __iter__(self):return selfdef __next__(self):self.counter += 1if self.counter == self.num:raise StopIteration()return self.counter
      class Xrange(object):def __init__(self, max_num):self.max_num = max_numdef __iter__(self):return IterRange(self.max_num)   # 可迭代对象obj = Xrange(100)
      for item in obj:print(item)

      基于可迭代对象和生成器实现:自定义range。

      class Xrange(object):def __init__(self, max_num):self.max_num = max_numdef __iter__(self):counter = 0while counter > self.max_num:yield countercounter += 1
      obj = Xrange(100)
      for item in obj:print(item)
      

      补充:

      • Iterator检查对象是否是迭代器;Iterable,检查对象是否可以被迭代。
      • 所有的迭代器都是可迭代的,但并非所有的可迭代对象都是迭代器。
      from collections.abc import Iterator, Iterable
      v1 = [11, 22, 33]
      print( isinstance(v1, Iterator) )  # false,判断是否是迭代器;判断依据是__iter__ 和 __next__。
      v2 = v1.__iter__()
      print( isinstance(v2, Iterator) )  # True
      v1 = [11, 22, 33]
      print( isinstance(v1, Iterable) )  # True,判断依据是是否有 __iter__且返回迭代器对象。
      v2 = v1.__iter__()
      print( isinstance(v2, Iterable) )  # True,判断依据是是否有 __iter__且返回迭代器对象。
      

      三、补充

      1. 继承【补充】

      对于Python面向对象中的继承,我们已学过:

      • 继承存在意义:将公共的方法提取到父类中,有利于增加代码重用性。

      • 继承的编写方式:

        # 继承
        class Base(object):pass
        class Foo(Base):pass
        # 多继承
        class Base1(object):pass
        class Bar1(object):pass
        class Foo1(Base1,Bar1):pass
      • 调用类中的成员时,遵循:

        1. 优先在自己所在类中找,没有的话则去父类中找。
        2. 如果类存在多继承(多个父类),则先找左边再找右边。

      上述的知识点掌握之后,其实就可以解决继承相关的大部分问题。

      1.1 mro和c3算法

      如果类中存在继承关系,在可以通过mro()获取当前类的继承关系(找成员的顺序)。merge,与除了当前元素的其他元素比较是存在,若不存在就进行剔除.

      示例1:

      #############################################
      mro(A) = [A] + [B,C]
      mro(A) = [A,B,C]
      #############################################
      mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
      mro(A) = [A] + merge( [object], [object], [] )
      mro(A) = [A] + [B,C,object]
      mro(A) = [A,B,C,object]
      class C(object):pass
      class B(object):pass
      class A(B, C):pass
      print( A.mro() )   # [<class '__main__.A'>,<class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
      print( A.__mro__ ) # (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
      

      示例2:

      mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
      mro(A) = [A] + merge( [], [D], [] )
      mro(A) = [A] + [B,C,D]
      mro(A) = [A,B,C,D]
      
      class D(object):pass
      class C(D):pass
      class B(object):pass
      class A(B, C):pass
      print( A.mro() ) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
      

      示例3:

      简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
      
      mro(A) = [A] + merge( mro(B),          mro(C),      mro(P),      [B,C,P])[]   [N]     [P]          [P]mro(A) = [A,B,D,G,H,K,C,E,F,M,N,P]-----------------------------------------------------
      mro(B) = [B] + merge( mro(D), mro(E), [D,E])
      mro(D) = [D] + merge(mro(G),mro(H), [G,H])
      mro(G) = [G]
      mro(H) = [H,K]
      mro(B) = [B] + merge( [], [E,M], [E])
      mro(B) = [B,D,G,H,K,E,M]-----------------------------------------------------
      mro(C) = [C] + merge(mro(E),mro(F),[E,F])
      mro(E) = [E,M]
      mro(F) = [F,M,N] 
      mro(C) = [C] + merge([M],[M,N] ,[])
      mro(C) = [C,E,F,M,N]
      
      class M:passclass N:passclass E(M):passclass G:passclass K:passclass H(K):passclass D(G, H):passclass F(M, N):passclass P:passclass C(E, F):passclass B(D, E):passclass A(B, C, P):passprint(A.mro()) # 简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
      

      特别补充:一句话搞定继承关系

              不知道你是否发现,如果用正经的C3算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类也要分析很久。所以,我自己根据经验总结了一句话赠送给大家:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。

      1.2 py2和py3区别(了解)

      • 在python2.2之前,只支持经典类从左到右,深度优先,大小钻石,不留顶端】未继承object类型。

      • 后来,Python想让类默认继承object(其他语言的面向对象基本上也都是默认都继承object),此时发现原来的经典类不能直接集成集成这个功能,有Bug。

      • 所以,Python决定不再原来的经典类上进行修改了,而是再创建一个新式类来支持这个功能。【从左到右,深度优先,大小钻石,留住顶端。-----C3算法

        1. 经典类,不继承object类型

          class Foo:pass
          
        2. 新式类,直接或间接继承object

          class Base(object):passclass Foo(Base):pass
          
      • 这样,python2.2之后 中就出现了经典类和新式类共存。(正式支持是2.3)

      • 最终,python3中丢弃经典类,只保留新式类。

      2. 内置函数补充

      本次要给讲解的内置函数共8个,他们都跟面向对象的知识相关。

      • classmethod、staticmethod、property 。

      • callable,是否可在后面加括号执行。所以当你以后在见到下面的情况时,首先就要想到handler可以是:函数、类、具有call方法的对象 这三种,到底具体是什么,需要根据代码的调用关系才能分析出来。

      • super,按照mro继承关系向上找成员。

      • type,获取一个对象的类型。

      • isinstance,判断对象是否是某个类或其子类的实例。

      • issubclass,判断类是否是某个类的子孙类。

      3.异常处理

      在程序中如果遇到一些 不可预知 的错误,可以选择用异常处理来做。

      import requestswhile True:url = input("请输入要下载网页地址:")res = requests.get(url=url)with open('content.txt', mode='wb') as f:f.write(res.content)
      

      上述下载视频的代码在正常情况下可以运行,但如果遇到网络出问题,那么此时程序就会报错无法正常执行。

      异常处理的基本格式:

      try:# 逻辑代码
      except Exception as e:# try中的代码如果有异常,则此代码块中的代码会执行。
      
      try:# 逻辑代码
      except Exception as e:# try中的代码如果有异常,则此代码块中的代码会执行。
      finally:# try中的代码无论是否报错,finally中的代码都会执行,一般用于释放资源。"""
      try:file_object = open("xxx.log")# ...
      except Exception as e:# 异常处理
      finally:file_object.close()  
      """    
      

              try中没异常,最后执行finally关闭文件;try有异常,执行except中的逻辑,最后再执行finally关闭文件

      3.1 异常细分

              如果想要对错误进行更新的处理,例如:发生Key错误和发生Value错误分开处理。Python中内置了很多细分的错误,供你选择。

      常见异常:
      """
      AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
      IOError 输入/输出异常;基本上是无法打开文件
      ImportError 无法引入模块或包;基本上是路径问题或名称错误
      IndentationError 语法错误(的子类) ;代码没有正确对齐
      IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
      KeyError 试图访问字典里不存在的键
      KeyboardInterrupt Ctrl+C被按下
      NameError 使用一个还未被赋予对象的变量
      SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
      TypeError 传入对象类型与要求的不符合
      UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
      导致你以为正在访问它
      ValueError 传入一个调用者不期望的值,即使值的类型是正确的
      """
      更多异常:
      """
      ArithmeticError
      AssertionError
      AttributeError
      BaseException
      BufferError
      BytesWarning
      DeprecationWarning
      EnvironmentError
      EOFError
      Exception
      FloatingPointError
      FutureWarning
      GeneratorExit
      ImportError
      ImportWarning
      IndentationError
      IndexError
      IOError
      KeyboardInterrupt
      KeyError
      LookupError
      MemoryError
      NameError
      NotImplementedError
      OSError
      OverflowError
      PendingDeprecationWarning
      ReferenceError
      RuntimeError
      RuntimeWarning
      StandardError
      StopIteration
      SyntaxError
      SyntaxWarning
      SystemError
      SystemExit
      TabError
      TypeError
      UnboundLocalError
      UnicodeDecodeError
      UnicodeEncodeError
      UnicodeError
      UnicodeTranslateError
      UnicodeWarning
      UserWarning
      ValueError
      Warning
      ZeroDivisionError
      """
      

      3.2 自定义异常和抛出异常

              上面都是Python内置的异常,只有遇到特定的错误之后才会抛出相应的异常。其实,在开发中也可以自定义异常。

      class MyException(Exception):pass
      try:pass
      except MyException as e:print("MyException异常被触发了", e)
      except Exception as e:print("Exception", e)
      

              上述代码在except中定义了捕获MyException异常,但他永远不会被触发。因为默认的那些异常都有特定的触发条件,例如:索引不存在、键不存在会触发IndexError和KeyError异常。对于我们自定义的异常,如果想要触发,则需要使用:raise MyException()类实现。

      class MyException(Exception):def __init__(self, msg, *args, **kwargs):super().__init__(*args, **kwargs)self.msg = msgtry:raise MyException("xxx失败了")
      except MyException as e:print("MyException异常被触发了", e.msg)
      except Exception as e:print("Exception", e)
      
      class MyException(Exception):title = "请求错误"
      try:raise MyException()
      except MyException as e:print("MyException异常被触发了", e.title)
      except Exception as e:print("Exception", e)
      

      示例

      class IT(object):def __init__(self):self.count = 0def __iter__(self):return selfdef __next__(self):self.count += 1if self.count == 3:raise StopIterationreturn self.count
      obj=IT()
      while True:try:i=next(obj)except StopIteration as e:print("数据获取完毕")breakprint(i)

      3.3 特殊的finally

              当在函数或方法中定义异常处理的代码时,要特别注意finally和return。在try或except中即使定义了return,也会执行最后的finally块中的代码。

      def func():try:return 123except Exception as e:passfinally:print(666)print(func())  #先执行finally,再执行return
      

       本   篇   完   结   …  …


      持     续     更     新     中   …    … 

      本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
      如若转载,请注明出处:http://www.pswp.cn/diannao/89849.shtml
      繁体地址,请注明出处:http://hk.pswp.cn/diannao/89849.shtml

      如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

      相关文章

      【Qt】qml组件对象怎么传递给c++

      将QML组件对象传递给C的方法 在QML和C之间传递完整的组件对象需要特殊处理&#xff0c;因为QML组件是动态创建的JavaScript对象。以下是几种有效的方法&#xff1a; 1. 使用QObject指针传递 C端设置 // MyClass.h #include <QObject> #include <QQuickItem>cla…

      Java基础 集合框架 List框架

      list架构 list接口list 核心特性以及扩展Collection的体现 抽象类 AbstractList抽象类 AbstractSequentialList (简化链表的顺序访问)AbstractSequentialList 核心特点自定义实现示例代码讲解其实现原理AbstractSequentialList 总结与AbstractList的对比 List 实现类 ArrayList…

      2025年6月28和29日复习和预习(C++)

      学习笔记大纲​一、预习部分&#xff1a;数组基础​&#xff08;一&#xff09;核心知识点​数组的创建&#xff1a;掌握一维数组的声明方式&#xff0c;如int arr[5];&#xff08;创建一个包含 5 个整数的数组&#xff09;。重点在于理解数组长度需为常量&#xff0c;且在声明…

      【centos8服务如何给服务器开发3306端口】

      在 CentOS 8 中开放 MySQL 默认端口 3306&#xff0c;需要配置防火墙和 SELinux。以下是详细步骤&#xff1a; 1. 开放防火墙端口&#xff08;Firewalld&#xff09; CentOS 8 默认使用 firewalld 管理防火墙&#xff0c;执行以下命令开放 3306 端口&#xff1a; # 开放 TCP 33…

      python系列之:使用md5和sha256完成签名认证,调用接口

      python系列之:使用md5和sha256完成签名认证,调用接口 MD5签名和sha256签名认证md5认证代码sha256认证代码拼接签名生成签名拼接url调用接口MD5签名和sha256签名认证 MD5签名认证 算法特性: 生成128位(16字节)的哈希值计算速度快已被证明存在碰撞漏洞(不同输入可能产生相同…

      SpringBatch配置与入门实例

      通过对SpringBatch基础概念的了解&#xff0c;参考&#xff1a;SpringBatch使用介绍 任何技术用起来之后&#xff0c;再去探究内部细节的原理&#xff0c;才会事半功倍。下面记录一下笔者在SpringBoot项目中集成SpringBatch&#xff0c;并且通过一个小的实例展示如何简单使用它…

      spdlog 项目介绍与二次封装

      目录 介绍 二次封装 介绍 spdlog 是C开源的第三方日志库&#xff0c;整个项目在 spdlog 命名空间中。 在 spdlog 命名空间的 level 命名空间里定义了枚举类型&#xff0c;把日志分为了 5 个等级&#xff1a;trace debug info warn err critical enum level_enum : in…

      shell编程之awk命令详解

      1. awk 教程 1.1 调用 awk awk 是一种强大的文本处理工具&#xff0c;在 Linux 系统中广泛应用于日志分析、数据处理等场景。调用 awk 主要有以下三种方式&#xff1a; 1.1.1 命令行方式 基本语法为&#xff1a; awk (-F filed-separator) commands input-files其中&#…

      服务器需要备案吗?在哪些地区需要备案?

      &#x1f3af; 服务器是否需要备案&#xff1f; 是否需要备案&#xff0c;关键看以下两个因素&#xff1a; 服务器所在地&#xff08;机房位置&#xff09; 网站面向的访问群体&#xff08;境内或境外&#xff09; &#x1f3f7; 中国大陆&#xff08;境内&#xff09;服务器…

      HarmonyOS学习3---ArkUI

      1、组件 1.1、基础组件 1.2、布局容器 1.3、页面导航 1.4、其他组件 2、ArkTs/C混合开发&#xff0c;高性能编码 3、布局能力&交互归一 4、实时开发预览

      Java学习第十五部分——MyBatis

      目录 一.概述 二.特点 三.组件 四.Mapper 五.配置文件 六.使用步骤 七.高级功能 八.优点缺点 九.项目实战 1.打开idea创建一个Java项目&#xff0c;构建系统选“Maven”​ 2.创建完成后若依赖报错&#xff0c;可通过下载或重新加载来解决​ 3.配置pom.xml文件&…

      小企业如何搭建本地私有云服务器,并设置内部网络地址提供互联网访问

      在数字化时代&#xff0c;很多普通公司小企业规模的&#xff0c;利用本地小型服务器或计算机搭建私有云服务器&#xff0c;不仅可以提升数据管理效率&#xff0c;还能保障业务数据的安全性和灵活性。以下是为小企业量身定制的私有云服务器搭建指南&#xff0c;及最后附无公网IP…

      MySQL 八股文【持续更新ing】

      MySQL 八股文【持续更新ing】 文章目录 MySQL 八股文【持续更新ing】前言一、MySQL的存储引擎有哪些&#xff1f;他们之间有什么区别&#xff1f;二、MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别&#xff1f;1.InnoDB 中的聚簇索引2.InnoDB 中的非聚簇索引 三、MySQL…

      每日算法刷题Day42 7.5:leetcode前缀和3道题,用时2h

      7. 3026.最大好子数组和(中等,学习) 3026. 最大好子数组和 - 力扣&#xff08;LeetCode&#xff09; 思想 1.给你一个长度为 n 的数组 nums 和一个 正 整数 k 。 如果 nums 的一个子数组中&#xff0c;第一个元素和最后一个元素 差的绝对值恰好 为 k &#xff0c;我们称这个…

      Linux操作系统之文件(四):文件系统(上)

      前言&#xff1a; 我们前几篇文章讲了缓冲区与重定向的有关概念&#xff0c;这些设计是linux系统的核心机制&#xff0c;对系统性能、资源管理和用户操作灵活性有重要意义。 不涉及一些硬件就不可能让大家清楚地去理解文件系统&#xff0c;所以这篇文章&#xff0c;我将会从计…

      java中,stream的filter和list的removeIf筛选速度比较

      在 Java 里&#xff0c;Stream 的filter和 List 的removeIf筛选效率要依据具体情形来判断。 1. 操作本质有别 Stream 的 filter&#xff1a; 它是一种中间操作&#xff0c;不会立刻执行&#xff0c;而是把筛选条件记录下来。只有遇到终端操作时&#xff0c;才会开始处理元素。…

      Python(28)Python循环语句指南:从语法糖到CPython字节码的底层探秘

      目录 引言一、推导式家族全解析1.1 基础语法对比1.2 性能对比测试 二、CPython实现揭秘2.1 字节码层面的秘密2.2 临时变量机制 三、高级特性实现3.1 嵌套推导式优化3.2 条件表达式处理 四、性能优化指南4.1 内存使用对比4.2 执行时间优化技巧 五、最佳实践建议六、总结&#x1…

      深度分析:Microsoft .NET Framework System.Random 的 C++ 复刻实现

      深度分析&#xff1a;Microsoft .NET Framework Random 的 C 复刻实现 核心原理与算法结构 本实现基于 Knuth 减随机数生成器&#xff08;Subtractive Random Number Generator&#xff09;&#xff0c;是 .NET Framework 中 System.Random 的精确复刻。其核心特点包括&#x…

      [论文阅读] 人工智能 | 在非CUDA硬件上运行几何学习:基于Intel Gaudi-v2 HPU的PyTorch框架移植实践

      在非CUDA硬件上运行几何学习&#xff1a;基于Intel Gaudi-v2 HPU的PyTorch框架移植实践 论文标题&#xff1a;PyTorch-based Geometric Learning with Non-CUDA Processing Units: Experiences from Intel Gaudi-v2 HPUs arXiv:2507.01031 (cross-list from cs.LG) PyTorch-ba…

      Python-多线程-threading

      1 需求 2 接口 3 示例 4 参考资料 Python treading 模块 | 菜鸟教程