• Python灰帽编程——错误异常处理和面向对象


    1. 错误和异常

    如果遇到了错误(异常),如何去处理?

    1.1 基本概念

    1.1.1 Python 异常

    当程序运行时,因为遇到未知的错误而导致中止运行,便会出现Traceback 消息,打印异常。异常即是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。一般情况下,在Python 无法正常处理程序时就会发生一个异常。异常是Python 对象,表示一个错误。当Python 脚本发生异常时我们需要响应处理它,否则程序会终止执行可以捕获异常。

    异常描述
    SyntaxError语法错误
    NameError未声明/初始化对象
    IndexError序列中没有此索引
    KeyboardInterrupt用户中断执行(Ctrl+C)
    EOFError没有内建输入,到达EOF 标记(Ctrl+D) 不适用windows 系统
    IOError输入/输出操作失败
    ValueError当操作或函数接收到具有正确类型但值不适合的参数, 并且情况不能用更精确的异常,例如 IndexError来描述时将被引发。
    TypeError字符串与整数相加时触发。

    1.2 检测(捕获)异常

    如果一个语句发生了错误或异常,跳过该语句的执行,执行另外的语句。

    1.2.1 try except 语句

    监控异常

    尝试执行try 语句,如果遇到异常(行不通)则执行except 语句。两个语句执行一个。

    语法规则

    try:
        pass                # 监控这里的异常
    except Exception[, reason]:
        pass                # 异常处理代码
    
    • 1
    • 2
    • 3
    • 4

    示例:

    # 01 - 异常初探.py
    
    try:		#尝试执行语句
        username = input("Please input your name: ")
        print(f"Welcome, {username}")
    
    except:		#如果遇到异常执行的语句
        print("\nSomething Error!")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1.2.2 捕获多种异常

    算命脚本:输入年龄,预测明年的年龄。

    可以把多个except 语句连接在一起,处理一个try 块中可能发生的多种异常。

    # 02 - 捕获多种异常.py
    
    banner = '''
    杰哥的算命脚本
    1. 预测年龄
    2. 预测财运
    3. 预测姻缘
    '''
    
    print(banner)
    
    choice = input("Please input the number: ")
    choice = int(choice)
    
    def guess_age():
        try:
            age = input("Please input your age: ")
            print(f"The next year your name: {int(age) + 1}")
    
        except ValueError:
            print("\nPlease input a number!")
    
        except KeyboardInterrupt:
            print("\nCtrl + C.END")
    
        except:
            print("\nSomething Error!")
    if choice != 1:
        print("好好学习...")
        exit()
    eles:
        guess_age()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    1.2.3 捕获所有异常

    如果出现的异常没有出现在指定要捕获的异常列表中,程序仍然会中断。可以使用在异常继承的树结构中,BaseException 是在最顶层的,所以使用它可以捕获任意类型的异常。

    except BaseException:           # 捕获所有异常,相当于except
        print("\nSomething Error!")
    
    • 1
    • 2

    Pyhton 异常树

    BaseException                                               所有异常的基类
         |
         |
         +-- SystemExit                                         解释器请求退出
         |
         |
         +-- KeyboardInterrupt                                  用户中断执行(通常是输入^C)
         |
         |
         +-- GeneratorExit                                      生成器调用close();方法时触发的
         |
         |
         +-- Exception                          常规错误的基类,异常都是从基类Exception继承的。
              |
              +-- StopIteration                                 迭代器没有更多的值
              |
              +-- StandardError                                 所有的内建标准异常的基类
              |    +-- BufferError                              缓冲区操作不能执行
              |    +-- ArithmeticError                          所有数值计算错误的基类
              |    |    +-- FloatingPointError                  浮点计算错误
              |    |    +-- OverflowError                       数值运算超出最大限制
              |    |    +-- ZeroDivisionError                   除(或取模)零 (所有数据类型)
              |    +-- AssertionError                           断言语句失败
              |    +-- AttributeError                           访问未知对象属性
              |    +-- EnvironmentError                         操作系统错误的基类
              |    |    +-- IOError                             输入输出错误
              |    |    +-- OSError                             操作系统错误
              |    |         +-- WindowsError (Windows)         系统调用失败
              |    |         +-- VMSError (VMS)                 系统调用失败
              |    +-- EOFError                                 没有内建输入,到达EOF 标记
              |    +-- ImportError                              导入模块/对象失败
              |    +-- LookupError              无效数据查询的基类,键、值不存在引发的异常
              |    |    +-- IndexError                          索引超出范围
              |    |    +-- KeyError                            字典关键字不存在
              |    +-- MemoryError              内存溢出错误(对于Python 解释器不是致命的)
              |    +-- NameError                未声明/初始化对象 (没有属性)
              |    |    +-- UnboundLocalError                   访问未初始化的本地变量
              |    +-- ReferenceError           弱引用(Weak reference)试图访问已经垃圾回收了的对象
              |    +-- RuntimeError                             一般的运行时错误
              |    |    +-- NotImplementedError                 尚未实现的方法
              |    +-- SyntaxError                              语法错误
              |    |    +-- IndentationError                    缩进错误
              |    |         +-- TabError                       Tab 和空格混用
              |    +-- SystemError                              一般的解释器系统错误
              |    +-- TypeError                                对类型无效的操作
              |    +-- ValueError                               传入无效的参数
              |         +-- UnicodeError                        Unicode 相关的错误
              |              +-- UnicodeDecodeError             Unicode 解码时的错误
              |              +-- UnicodeEncodeError             Unicode 编码时错误
              |              +-- UnicodeTranslateError          Unicode 转换时错误
              |
              +-- Warning                                       警告的基类
                   +-- DeprecationWarning                       关于被弃用的特征的警告
                   +-- PendingDeprecationWarning                关于特性将会被废弃的警告
                   +-- RuntimeWarning              可疑的运行时行为(runtime behavior)的警告
                   +-- SyntaxWarning                            可疑的语法的警告
                   +-- UserWarning                              用户代码生成的警告
                   +-- FutureWarning                            关于构造将来语义会有改变的警告
                   +-- ImportWarning               关于模块进口可能出现错误的警告的基类。
                   +-- UnicodeWarning                           有关Unicode警告的基类。
                   +-- BytesWarning                             有关字节警告相关的基类。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    1.3 处理异常

    try:                                                # 尝试执行某个语句
        num = int(input("The number:"))
    except BaseException:                               # 如果遇到异常,执行的语句
        print("something error!")
    else:                                               # 如果没有遇到异常,执行的语句
        print(num)
    finally:                                            # 不管是否遇到异常,都要执行的语句。
        print("This is finally")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • else 子句:在try 范围中没有异常被检测到时,执行else 子句。在else 范围中的任何代码运行前,try 范围中的所有代码必须完全成功。

    • finally 子句:finally 子句是无论异常是否发生,是否捕捉都会执行的一段代码。比如获取用户的输入,写入文件,但是如果在获取数据过程中,发生了异常,就会导致整个脚本结束执行,文件也无法关闭了。为了解决这个问题,可以采用异常处理中的finally 子句,也就是说,无论语句是否正常执行,都需要关闭。

    # 03 - 处理异常.py
    
    banner = '''
    杰哥的算命脚本
    1. 预测年龄
    2. 预测财运
    3. 预测姻缘
    '''
    
    print(banner)
    
    choice = input("Please input the number: ")
    choice = int(choice)
    
    def guess_age():
        try:
            age = input("Please input your age: ")
            age = int(age) 
    
        except ValueError:
            print("\nPlease input a number!")
    
        except KeyboardInterrupt:
            print("\nCtrl + C")
    
        except:
            print("\nSomething Error!")
    
        else:
            print(f"The next year your name: {age + 1}")
    
        finally:
            print("杰哥算命脚本执行结束,祝你好运!")
    if choice != 1:
        print("好好学习...")
        exit()
    else:
        guess_age()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    1.4 特殊场景

    1.4.1 with 语句

    with 语句是用来简化代码的。比如在将打开文件的操作放在with 语句中,代码块结束后,文件将自动关闭。用来简化文件操作的打开和关闭,其中closed 属性是判断文件是否被关闭的

    >>> with open('foo.py') as f:
    
    • 1

    2. 内网主机存活检测程序

    网络模型:

    应用层
    表示层
    会话层			应用层			应用层		http|ftp|dns|dhcp
    传输层			传输层			传输层		tcp|udp
    网络层			网络层			网络层		ip
    数据链路层	  数据链路层
    物理层			物理层			网络接口层
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.1 scapy 模块

    与scrapy 有非常大的区别。

    scapy 是一个Python 的第三方模块,被称为“网络神器”。scapy 模块能够发送、捕获、分析和铸造网络数据包。

    2.1.1 主要功能

    image-20230918164702051

    2.1.2 scapy 安装

    Windows 下安装scapy

    python -m pip install scapy
    
    • 1

    依赖

    Kali 中自带scapy 环境。

    2.1.3 进入scapy 模块

    ┌──(ajest zh-CN)-[~]
    └─$ sudo scapy                     
    
                         aSPY//YASa       
                 apyyyyCY//////////YCa       |
                sY//////YSpcs  scpCY//Pp     | Welcome to Scapy
     ayp ayyyyyyySCP//Pp           syY//C    | Version 2.4.5
     AYAsAYYYYYYYY///Ps              cY//S   |
             pCCCCY//p          cSSps y//Y   | https://github.com/secdev/scapy
             SPPPP///a          pP///AC//Y   |
                  A//A            cyP////C   | Have fun!
                  p///Ac            sC///a   |
                  P////YCpc           A//A   | Craft packets before they craft
           scccccp///pSP///p          p//Y   | you.
          sY/////////y  caa           S//P   |                      -- Socrate
           cayCyayP//Ya              pY/Ya   |
            sY/PsY////YCc          aC//Yp 
             sc  sccaCY//PCypaapyCP//YSs  
                      spCPY//////YPSps    
                           ccaacs         
                                           using IPython 7.22.0
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.1.4 简单使用

    构造数据包

    >>> pkt = IP()/TCP()
    >>> pkt.show()
    ###[ IP ]### 
      version   = 4
      ihl       = None
      tos       = 0x0
      len       = None
      id        = 1
      flags     = 
      frag      = 0
      ttl       = 64
      proto     = tcp
      chksum    = None
      src       = 127.0.0.1
      dst       = 127.0.0.1
      \options   \
    ###[ TCP ]### 
         sport     = ftp_data
         dport     = http
         seq       = 0
         ack       = 0
         dataofs   = None
         reserved  = 0
         flags     = S
         window    = 8192
         chksum    = None
         urgptr    = 0
         options   = ''
    
    >>> pkt = IP(src = "192.168.1.11", dst = "192.168.1.1")/TCP()		#src:发送方dst:接收方
    >>> pkt.show()
    ###[ IP ]### 
      version   = 4
      ihl       = None
      tos       = 0x0
      len       = None
      id        = 1
      flags     = 
      frag      = 0
      ttl       = 64
      proto     = tcp
      chksum    = None
      src       = 192.168.1.11
      dst       = 192.168.1.1
      \options   \
    ###[ TCP ]### 
         sport     = ftp_data
         dport     = http
         seq       = 0
         ack       = 0
         dataofs   = None
         reserved  = 0
         flags     = S
         window    = 8192
         chksum    = None
         urgptr    = 0
         options   = ''
    
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    发送数据包

    发送数据包的函数说明
    sr(pkt)发送数据包,接收所有返回包
    sr1(pkt)发送数据包,接收(等待)一个返回包
    send(pkt)发送数据包,不等待返回包
    srp(pkt)发送2 层数据包,等待回应
    sendp(pkt)发送2 层数据包,不等待返回包
    >>> res = sr1(pkt)
    Begin emission:
    Finished sending 1 packets.
    .*
    Received 2 packets, got 1 answers, remaining 0 packets
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在发送数据包的时候,再接收返回包,网卡一直处于监听状态,接收监听的数据包,一直等待回应。直到接收到返回包。接到返回包,主机在线,接不到返回包,主机不在线。

    查看返回包

    >>> res.show()
    ###[ IP ]### 
      version   = 4
      ihl       = 5
      tos       = 0x0
      len       = 44
      id        = 13990
      flags     = 
      frag      = 0
      ttl       = 255
      proto     = tcp
      chksum    = 0x46a4
      src       = 10.9.21.1
      dst       = 10.9.21.111
      \options   \
    ###[ TCP ]### 
         sport     = http
         dport     = ftp_data
         seq       = 1510518667
         ack       = 1
         dataofs   = 6
         reserved  = 0
         flags     = SA
         window    = 65535
         chksum    = 0x4f9c
         urgptr    = 0
         options   = [('MSS', 1460)]
    ###[ Padding ]### 
            load      = '\x00\x00'
    
    >>>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    2.2 主机存活检测程序

    icmp的回应包:0

    # 04 - 内网主机存活检测程序.py
    
    from scapy.all import *
    from scapy.layers.inet import *
    from termcolor import colored
    
    import logging
    logging.getLogger("scapy.runtime").setLevel(logging.ERROR)		#屏蔽信息的模块,scapy运行期间,只有ERROR才显示即其他的都不显示。
    
    netmask = "192.168.16."     #接收端的网络位
    src = "192.168.16.177"      #发送端
    
    for i in range(1, 255):
        dst = f"{netmask}{i}"        #接收端
    
        pkt = IP(src = src, dst = dst)/ICMP()	#创建数据包
        res = sr1(pkt, timeout = 1, verbose = False)	#发送数据包等待回应,等待一秒,一秒后没有回应当作不在线,禁止详细输出
        if res and res.type == 0:
            print(colored(f"\n{dst} is ALIVE!","green"))
        else: 
            print(f"\r{dst} is NOT LIVE!!!",end = "")		#输出目的主机不存活,end=""使结果原地输出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3. 面向对象编程

    编写POC平台:

    goby,nuclei			
    		yaml语言
    		
    pocsuite3			高仿msf
    		py语言
    		
    yakit
    		yak语言
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.1 类

    类是一类事物的统称,比如学生。对象就是类的实例化。

    类有属性(变量),比如学生的姓名、性别、年龄、成绩等,也就是编程里面的变量。

    类有方法(函数),比如学生的上课、下课等,也就是编程里面的函数。

    3.1.1 创建类

    类是一种数据结构,我们可以用它来定义对象,对象把数据值和行为特性融合在一起。Python 使用class 关键字来创建类:通常类名的第一个字母大写,推荐使用驼峰式命名法,单词首字母均大写。类有属性(变量)和方法(动作,函数)组成。

    class ClassName(bases):
        'class documentation string'    #'类文档字符串'
        class_suite                     # 类体
    
    • 1
    • 2
    • 3

    3.1.2 _init_ 方法

    __init__() 是类的实例(对象)创建后第一个被调用的方法,通常被用来进行对象中属性(变量)的初始化。设置实例的属性可以在实例创建后任意时间进行,但是通常情况下优先在__init__() 方法中实现。

    • 定义类型
    • 实例化对象(创建一个对象)
    • 初始化对象(第一次给对象属性赋值)
    # 05 - __init__.py
    
    class Stu():
        #self:是一个特殊的参数,他对类的实例(对象)本身。在类的方法中,只用self参数可以访问该对象的属性和方法
        def __init__(self, name, sex, age, score):
            self.name = name
            self.sex = sex
            self.age = age
            self.score = score
    
    stu1 = Stu(name = "XJ", sex = True, age = 24, score = 59.9)	#在传参的时候,stu1传给类中的self,name传给类中的name...在类中相当于:stu1.name = "XJ",stu1.sex = True,stu1.age = 24...
    
    print(f"{stu1.name} 的成绩是{stu1.score}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.2 方法

    3.2.1 绑定方法

    方法仅仅是类内部定义的函数,方法只有在其所属的类拥有实例时,才能被调用;任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象就是自己。

    类中的方法,也就是类中定义的函数,这个函数第一个参数永远是self,表示自己。

    # 06 - 绑定方法.py
    
    import time
    
    class Stu():
        def __init__(self, name, sex, age, score):
            self.name = name
            self.sex = sex
            self.age = age
            self.score = score
    
        def getSocre(self):
            print(f"{self.name} 的成绩是{self.score}")
    
        def goodGoodStudy(self):
            print("好好学习中...")
            time.sleep(10)
            self.score += 0.1
    
    
    stu1 = Stu(name = "LH", sex = True, age = 24, score = 59.9)
    stu2 = Stu(name = "HMM", sex = True, age = 24, score = 89.9)
    
    stu2.getSocre()
    stu2.goodGoodStudy()
    stu2.getSocre()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    3.3 继承

    3.3.1 子类继承

    继承描述了基类(祖先)的属性如何遗传给派生类(子孙),子类可以继承它的基类的任何属性,不管是数据属性还是方法。

    # 34 - 子类继承.py
    
    import time
    
    class Kid():
        def __init__(self, name = "", sex = "", age = ""):
            self.name = name
            self.age = age
            self.sex = sex
    
        def play(self):
            print("玩游戏中...")
    
    class Stu(Kid):
        def __init__(self,name = "", sex = "", age = "", score = ""):
            Kid.__init__(self, name, sex, age)
            self.score = score
    
        def get_score(self):
            print(f"{self.name} 的成绩是{self.score}")
    
        def good_good_study(self):
            print("好好学习中...")
            time.sleep(10)
            self.score += 0.1
    
    stu1 = Stu(name = "XJ", sex = True, age = 24, score = 59.9)
    stu2 = Stu(name = "LL", sex = True, age = 25, score = 49.9)
    stu3 = Stu(name = "HMM", sex = True, age = 23, score = 99.9)
    stu4 = Stu(name = "LH", sex = True, age = 24, score = 79.9)
    
    stu1.play()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3.3.2 方法重写

    如果子类中有和父类同名的方法,父类方法将被覆盖;如果需要访问父类的方法,则要调用一个未绑定的父类方法,明确给出子类的实例。

    # 08 - 方法重写.py
    
    import time
    
    class Kid():
        def __init__(self, name, sex, age):
            self.name = name
            self.sex = sex
            self.age = age
    
        def play(self):
            print("玩游戏中...")
    
    class Stu(Kid):
        def __init__(self, name, sex, age, score):
            Kid.__init__(self, name, sex, age)
            self.score = score	#Stu类继承了Kid类。在它的构造函数中,除了接受与Kid相同的三个参数外,还接受一个额外的参数score(分数)。Kid.__init__(self, name, sex, age)用于调用父类Kid的构造函数,以便初始化父类的属性。同时,Stu类还定义了一个自己的属性score,并将score的参数值赋给它。
    
        def getSocre(self):
            print(f"{self.name} 的成绩是{self.score}")
    
        def goodGoodStudy(self):
            print("好好学习中...")
            time.sleep(10)
            self.score += 0.1
    
        def play(self):
            print("玩王者荣耀中...")
            time.sleep(2)
            self.score -= 10
    
    stu1 = Stu(name = "AJEST", sex = True, age = 24, score = 59.9)
    stu2 = Stu(name = "HMM", sex = True, age = 24, score = 89.9)
    
    stu2.getSocre()
    stu2.play()
    stu2.getSocre()
    stu2.goodGoodStudy()
    stu2.goodGoodStudy()
    stu2.getSocre()
    stu2.play()
    stu2.getSocre()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3.3.3 多重继承

    Python 允许多重继承,即一个类可以是多个父类的子类,子类可以拥有所有父类的属性。

    # 09 - 多重继承.py
    
    class A():
        def a(self):
            print("This is function A!")
    
    class B():
        def b(self):
            print("This is function B!")
    
    class C(A, B):
        pass
    C = C()
    C.a()
    C.b()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    解释:

    • 在Python中,pass 是一个空语句,不执行任何操作。当你在代码中遇到需要写一段代码但又暂时没有实现的情况时,可以使用 pass 来占位,使得代码结构完整而没有语法错误。

    • 多重继承是指一个类可以继承自多个父类的特性和方法。在Python中,多重继承可以通过在类定义时指定多个父类来实现。

      当一个类继承自多个类时,它继承了这些父类的属性和方法。这意味着子类可以使用来自所有父类的特性,并且可以在自己的定义中添加新的属性和方法。

      下面是一个简单的示例,演示了多重继承的概念:

      class A:
          def method_a(self):
              print("这是A类的方法")
      
      class B:
          def method_b(self):
              print("这是B类的方法")
      
      class C(A, B):
          def method_c(self):
              print("这是C类的方法")
      
      # 创建C类的对象
      obj = C()
      
      # 调用来自父类A和B的方法
      obj.method_a()  # 输出:这是A类的方法
      obj.method_b()  # 输出:这是B类的方法
      
      # 调用自己的方法
      obj.method_c()  # 输出:这是C类的方法
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      在上面的代码中,类A和B分别定义了method_a和method_b两个方法。类C继承自A和B,因此它拥有这两个父类的方法。然后,通过创建C类的对象obj,我们可以调用来自父类A和B的方法,也可以调用C类自己的方法method_c。

      需要注意的是,当一个类继承自多个父类时,如果不同父类中存在同名的方法或属性,Python解释器将按照特定的顺序(称为方法解析顺序)来查找和调用。方法解析顺序由C3线性化算法确定,它确保解析顺序是一种合理且一致的方式。在Python中,可以使用c3_linearize()函数来查看类的方法解析顺序。

      多重继承的使用必须慎重,因为它可以引起一些复杂的问题,例如方法冲突和命名空间冲突。因此,在使用多重继承时,需要仔细考虑类与类之间的关系,以及方法和属性的命名以避免冲突。

    3.4 魔法函数

    3.4.1 类和实例的内建函数

    函数作用
    issubclass()判断一个类是另一个类的子类或子孙类
    isinstance()判定一个对象是否是另一个给定类(可以是一个父类的子孙类,也可以是一个子类的父类)的实例
    hasattr()判断一个对象是否有一个特定的属性
    getattr()获得一个对象的属性值
    setattr()设置一个对象的属性
    delattr()删除一个对象的属性

    3.4.2 常用的魔法函数

    魔法函数是系统自带的,会在“恰当”的时候自动调用。

    # 10 - 魔术方法.py
    
    class Test():
        def __init__(self):
            print("Function __init__ is called!")
    
        def __str__(self):
            return "Why print(self)?"
    
        def __call__(self):
            print("Why call me like Function?")
    
    t = Test()
    
    print(t)
    
    t()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    解释:

    在Python中,魔法函数(Magic methods)是一类特殊的函数,以双下划线(__)开头和结尾的方法。它们也被称为特殊方法或双下方法。

    魔法函数在Python中用于定义自定义类的行为,使其具有类似内置类型的行为。当某些特定的操作发生时(例如实例创建、对象调用、属性访问等),魔法函数会被自动调用。

    下面是一些常见的魔法函数的详细解释和示例:

    1. __init__: 这是一个构造函数,用于创建对象实例。它在对象被创建时自动调用,常用于初始化对象的属性。

      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
      person = Person("John", 30)
      print(person.name)  # 输出: John
      print(person.age)   # 输出: 30
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    2. __str____repr__: __str__ 用于定义对象的字符串表示形式,对于用户的友好输出有很大作用;而 __repr__ 则用于定义对象的技术细节表示形式,主要用于调试和开发。

      class Person:
          def __init__(self, name, age):
              self.name = name
              self.age = age
          
          def __str__(self):
              return f"Person(name={self.name}, age={self.age})"
          
          def __repr__(self):
              return f"Person(name='{self.name}', age={self.age})"
      
      person = Person("John", 30)
      print(str(person))   # 输出: Person(name=John, age=30)
      print(repr(person))  # 输出: Person(name='John', age=30)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    3. __len__: 该方法定义了对象的长度。使用内建函数 len() 调用对象时,实际上是调用了对象的 __len__ 方法。

      class MyList:
          def __init__(self, elements):
              self.elements = elements
          
          def __len__(self):
              return len(self.elements)
      
      my_list = MyList([1, 2, 3, 4, 5])
      print(len(my_list))  # 输出: 5
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. __getitem____setitem__: 这些方法定义了对象的索引操作行为。可以通过方括号 [] 访问对象中的元素,而实际上是调用了对应的魔法函数。

      class MyList:
          def __init__(self, elements):
              self.elements = elements
          
          def __getitem__(self, index):
              return self.elements[index]
          
          def __setitem__(self, index, value):
              self.elements[index] = value
      
      my_list = MyList([1, 2, 3, 4, 5])
      print(my_list[2])    # 输出: 3
      my_list[2] = 10
      print(my_list[2])    # 输出: 10
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    5. __call__: 使得对象可以像函数一样被调用。在对象后面加括号时,会自动调用该对象的 __call__ 方法。

      class Calculator:
          def __call__(self, a, b):
              return a + b
      
      calculator = Calculator()
      result = calculator(3, 5)
      print(result)  # 输出: 8
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

    还有很多其他的魔法函数,例如 __add__(用于定义对象相加操作)、__sub__(用于定义对象相减操作)、__eq__(用于定义对象相等比较操作)等等。通过定义这些魔法函数,可以使得自定义类的实例表现得更加像内置类型,提供更多的灵活性和可定制性。

    3.5 私有化

    Python 为类元素(属性和方法)的私有性提供初步的形式,由双下划线开始的属性在运行时被“混淆”,所以直接访问是不允许的。

    # 11 - 私有化.py
    
    # 06 - 绑定方法.py
    
    import time
    
    class Stu():
        def __init__(self, name, sex, age, score):
            self.name = name
            self.sex = sex
            self.age = age
            self.__score = score
    
        def getSocre(self):
            print(f"{self.name} 的成绩是{self.__score}")	#允许访问成绩
    
        def goodGoodStudy(self):
            print("好好学习中...")
            time.sleep(10)
            self.__score += 0.1
    
    stu1 = Stu(name = "AJEST", sex = True, age = 24, score = 59.9)
    stu2 = Stu(name = "HMM", sex = True, age = 24, score = 89.9)
    
    stu2.getSocre()
    ator()
       result = calculator(3, 5)
       print(result)  # 输出: 8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    还有很多其他的魔法函数,例如 __add__(用于定义对象相加操作)、__sub__(用于定义对象相减操作)、__eq__(用于定义对象相等比较操作)等等。通过定义这些魔法函数,可以使得自定义类的实例表现得更加像内置类型,提供更多的灵活性和可定制性。

  • 相关阅读:
    盲盒小程序开发:创新科技与消费者心理的完美结合
    算法竞赛进阶指南——0x15 字符串学习笔记
    spring boot 定时任务@Scheduled(cron = ““)不可用时并且注入失败时——笔记
    2024-05-10 Ubuntu上面使用libyuv,用于转换、缩放、旋转和其他操作YUV图像数据,测试实例使用I420ToRGB24
    Redis_03_Redis发布订阅(Pub/Sub)
    bgp的表与消息
    C语言 总结const的用法
    驱动 实现三盏灯开关
    如何实现sm3加密
    微服务架构之:基于Redis的分布式锁优化(Redisson)
  • 原文地址:https://blog.csdn.net/weixin_58954236/article/details/133064254