测开之面向对象进阶篇・《魔术方法》

Posted 七月的小尾巴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了测开之面向对象进阶篇・《魔术方法》相关的知识,希望对你有一定的参考价值。

魔术方法

在类方法中__init__ 方法有什么作用?

创建对象时,自动调用对创建的对象进行初始化设置。

什么是魔术方法?

在python中,像 __init__这类双下滑线开头和结尾的方法,统一称之为魔术方法。

注意:在我们自己封装方法时,千万不要用双下划线开头或者结尾的方法定义,魔术方法时python内部自己定义的方法。

创建单例模式

我们在创建类的时候,每次调用时,都会实例化一个对象,那么如果我们要求这个类只会被实例化一次,应该怎么做呢?

class Demo(object):
    __instance = None

    def __new__(cls, *args, **kwargs):
        # 通过重写new方法来控制类创建对象的次数
        if cls.__instance is None:
            instance = super().__new__(cls)
            cls.__instance = instance

        return cls.__instance


deom1 = Demo()
deom2 = Demo()
# 分别打印 demo1 和 demo2 的内存地址
print(id(deom1), id(deom2))

结果:
1401905908320 1401905908320

通过返回结果,我们看到他们调用的是同一个内存地址

单例模式在工作中的使用场景,假设我们类在工作中需要创建非常多的对象,不同的对象他们调用的内部方法都是相同的,比如日志模块。通常我们定义的日志模块,在不同的地方调用日志,都需要调用一个对象。在python中,如果我们通过类去创建对象,那么就是一个日志收集器。如果说我们创建多个的话,就会导致日志重复收集。

import os
import logging
from logging.handlers import TimedRotatingFileHandler, BaseRotatingHandler
import colorama

colorama.init()


class Logger:
    __instance = None
    sh = logging.StreamHandler()

    def __new__(cls, path=None, level='DEBUG'):
        """
        :param path: report path
        :param args:
        :param kwargs:
        :return:
        """
        if not cls.__instance:
            cls.__instance = super().__new__(cls)
            log = logging.getLogger('apin-ui')
            log.setLevel(level)
            cls.__instance.log = log
        return cls.__instance

    def set_level(self, level):
        """设置日志输出的等级"""
        self.log.setLevel(level)

    def set_file_handle(self, level, path):
        if path:
            if not os.path.isdir(path):
                os.mkdir(path)
            fh = TimedRotatingFileHandler(os.path.join(path, 'apin-ui.log'), when='d',
                                          interval=1, backupCount=7,
                                          encoding="utf-8")
            fh.setLevel(level)
            self.log.addHandler(fh)
            # 定义handler的输出格式
            formatter = logging.Formatter("%(asctime)s | 【%(levelname)s】 | : %(message)s")
            fh.setFormatter(formatter)

    def debug(self, message):
        self.fontColor('\\033[0;34m{}\\033[0;34m{}\\033[0;34m{}')
        self.log.debug(message)

    def info(self, message):
        self.fontColor('\\033[0;32m{}\\033[0;32m{}\\033[0;32m{}')
        self.log.info(message)

    def warning(self, message):
        self.fontColor('\\033[0;33m{}\\033[0;43m{}\\033[0;33m{}')
        self.log.warning(message)

    def error(self, message):
        self.fontColor('\\033[0;31m{}\\033[0;41m{}\\033[0;31m{}')
        self.log.error(message)

    def exception(self, message):
        self.fontColor('\\033[0;31m{}\\033[0;41m{}\\033[0;31m{}')
        self.log.exception(message)

    def critical(self, message):
        self.fontColor('\\033[0;35m{}\\033[0;45m{}\\033[0;35m{}')
        self.log.critical(message)

    def fontColor(self, color):
        # 不同的日志输出不同的颜色
        formatter = logging.Formatter(color.format("%(asctime)s| ", "【%(levelname)s】", " | : %(message)s"))
        self.sh.setFormatter(formatter)
        self.log.addHandler(self.sh)


def print_info(msg):
    print('\\033[0;32m{}'.format(msg))


def print_waring(msg):
    print('\\033[0;33m{}'.format(msg))


def print_error(msg):
    print('\\033[0;31m{}'.format(msg))


if __name__ == '__main__':
    logger = Logger()
    logger.debug('debu等级日志')
    logger.info('info日志')
    logger.warning('warning日志')
    logger.error('error日志')
    logger.critical('CRITICAL日志')

上下文管理器

在我们使用python中的with open方法去打开文件,有没有思考过,为什么with open方法打开文件,会自动关闭?

这里我们就需要了解到上下文管理器的概念:

上下文管理器是一个python对象,为操作提供了额外的上下文信息。这种额外信息,在使用 with 语句初始化上下文,以及完成 with 代码块中采用所有可调用的形式。

  • object.enter(self)
    输入与次对象相关的上下文。如果存在的话,with 语句将绑定该方法的返回值到该一句的as子句子指定的目标。
  • object.exit(self, exc_type, exc_val, exc_tb)
    exc_type: 异常类型
    exc_val: 异常值
    exc_tb:异常回溯追踪

下面我们来手动实现一个文件操作:

class OpenFile(object):
    """手写实现文件操作的上下文"""
    def __init__(self, filename, method):
        # 初始化打开文件
        self.file = open(filename, method)

    # 启动上下文时,讲打开的对象返回出去
    def __enter__(self):
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()

python 中 str 魔术方法的使用

再python中假设,我们定义一个列表,我们可以看到可以直接将列表中的数据打印出来。

li = list([11, 22, 33, 44])
print(li)

打印:[11, 22, 33, 44]

但是如果说我们定义一个类呢,再类中我们定义一个初始化方法,此时我们想要获取初始化方法的内容,我们给类定义一个实例化对象,然后进行打印,可以看到打印出来了对象的地址。

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

m = Myetest("test")
print(m)
结果:<__main__.Myetest object at 0x00000267CDDE8820>

那么我们如何实现打印初始化方法中的数据呢?可以使用 __str__方法。

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

    def __str__(self):
        return self.name


m = Myetest("test")
print(m)

结果:test

注意:__str__ 方法只能返回字符串类型的数据,如果是其他类型数据,会抛异常

以上是关于测开之面向对象进阶篇・《魔术方法》的主要内容,如果未能解决你的问题,请参考以下文章

测开之面向对象进阶篇・《属性自省》

测开之数据类型进阶篇・第三篇《推导式》

测开之函数进阶篇・第七篇《装饰器》

测开之数据类型进阶篇・第四篇《生成器和迭代器》

测开之数据类型进阶篇・第二篇《字典和集合的原理应用》

测开之函数进阶篇・第六篇《闭包》