【问题标题】:How to write two decorator in file operations如何在文件操作中编写两个装饰器
【发布时间】:2019-09-12 12:01:57
【问题描述】:

我的 csv 文件包含以下内容

101,item_1 101,item_1

如果是 csv 我下面的代码将执行

import csv    
fName = input()
def read_csv(fName):
    try:
        with open(fName, 'r') as f:
            reader = csv.reader(f)
            for row in reader:
                print (row)

read_csv(fName)

这里是如何在decorator函数中编写异常并在上面调用。

第一个装饰器

如果fName 不以.txt.csv 结尾,则它必须生成输出not accept

第二个装饰器

如果 fName = file.txt 文本文件,则必须注意以下操作

def read_txt(fName):
    f = open(fName, "r")
    print(f.readline())

如果 csv 则执行第一个函数,如果 txt 则执行下一个函数。如何使用装饰器来实现。我可以设置 if 条件来实现这种情况,但事实并非如此

我没有装饰器的整个代码如下

fName = input()
def read_csv(fName):
    if fName.endswith('.csv'):
        #print  ('hi')
        try:
            with open(fName, 'r') as f:
                reader = csv.reader(f)
                for row in reader:
                    print (row)
        except IOError:
            print ("Could not read file:", fName)
    #SECOND DECORATOR
    if fName.endswith('.txt'):
        f = open(fName, "r")
        print(f.readline())
    #FIRST DECORATOR
    if not(fName.endswith('.csv')) and not(fName.endswith('.txt')):
        print ('not accept')
read_csv(fName)

【问题讨论】:

  • 这个问题到底是什么?
  • 如果 csv 则执行第一个函数,如果 txt 则执行下一个函数。如何使用装饰器实现
  • 这是作业吗?当有更好的选择时,不清楚为什么需要为此使用装饰器。

标签: python function file file-io decorator


【解决方案1】:

你可以用装饰器这样做:

import functools


def check_arguments(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        fname = kwargs['fname']
        if not fname.endswith('.csv') and not fname.endswith('.txt'):
            print('not accept')
        return func(*args, **kwargs)
    return wrapper


def set_file_processor(func):
    def read_csv(fname):
        print('read_csv', fname)

    def read_txt(fname):
        print('read_txt', fname)


    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        fname = kwargs['fname']
        if fname.endswith('.csv'):
            read_csv(fname)
        elif fname.endswith('.txt'):
            read_txt(fname)
        return func(*args, **kwargs)
    return wrapper


@check_arguments
@set_file_processor
def process(fname):
    pass


process(fname='input.csv')

【讨论】:

    【解决方案2】:

    您的问题似乎不在装饰器下,而是在工厂模式下,即根据输入文件进行不同的处理。

    下面的代码是一个非常简单和基本的工厂模式解决你的问题,这应该根据你的需要进行相应的修改,

    import os
    from abc import ABC, abstractmethod
    
    
    class FileProcessor(ABC):
        @abstractmethod
        def process():
            pass
    
    class TextFileProcessor(FileProcessor):
        def process(self, file_path):
            print("Text file processing goes here")
    
    
    class CsvFileProcessor(FileProcessor):
        def process(self, file_path):
            print("CSV file processing goes here")
    
    
    class DefaultFileProcessor(FileProcessor):
        def process(self, file_path):
            raise ValueError("File %s is not valid" % file_path)
    
    
    class FileFactory:
        processors = {
            'txt': TextFileProcessor,
            'csv': CsvFileProcessor,
            'default': DefaultFileProcessor
        }
    
        def __init__(self, file_path):
            if not os.path.exists(file_path):
                raise IOError("File not found")
            self.file_path = file_path
    
        def process(self):
            dot_splits = self.file_path.split(".")
            ext = dot_splits[-1] if len(dot_splits) > 1 else "default"
            ext = ext if ext in self.processors else "default"
            processor_class = self.processors.get(ext)
    
            return processor_class().process(self.file_path)
    
    
    FileFactory(file_path).process()
    

    后期如果你想添加json处理器,也可以通过添加来轻松完成

    processors = {
        'txt': TextFileProcessor,
        'csv': CsvFileProcessor,
        'json': JsonFileProcessor,
        'default': DefaultFileProcessor
    }
    

    并创建新的 Json 处理器类,

    class JsonFileProcessor(FileProcessor):
        def process(self, file_path):
            print("JSON file processing goes here")
    

    【讨论】:

    • 完美工作,但我需要使用装饰器来实现
    • 对于上述问题,装饰器根本不是解决方案,问题是有一个条件函数调用-即-如果是CSV,则为一个函数,如果不是,则应为另一个函数调用。装饰器不应该为此而设计,因为它旨在解决常见的可重复问题(如检查身份验证)/可读性。但是,如果您正在寻找一个装饰器链来为同一个对象处理不同的事情,请参阅w3resource.com/python-exercises/…
    【解决方案3】:

    根据您的代码和this very useful guide,这是一个可能的解决方案:

    def read_file_decorator(fName):
        def read_csv():
            print('read_csv')
            with open(fName, 'r') as f:
                reader = csv.reader(f)
                for row in reader:
                    print(row)
    
        def read_txt():
            print('read_txt')
            f = open(fName, 'r')
            for row in f:
                print(row)
    
        if fName.endswith('.csv'):
            return read_csv
        elif fName.endswith('.txt'):
            return read_txt
        else:
            return None
    
    reader_function = read_file_decorator(fileName)
    if reader_function != None:
        reader_function()
    else:
        print('not accept')
    

    我使用有状态装饰器在实际执行之前记住读取器函数中的文件名(为了不传递两次);我将固定值None 用于无效文件类型。

    【讨论】:

      【解决方案4】:

      根据需求使用装饰器将是装饰器的过度杀伤力。但是如果必须使用装饰器来实现它,我们可以这样实现:

      • 我们可以创建一个名为read_file 的虚拟函数和一个名为reader 的装饰器函数
      • 用户将始终以文件名作为参数调用read_file,装饰器函数reader将检查传递的文件扩展名并调用所需的函数-read_csvread_text
      def reader(fun):
          def wrapper(*args):
              fname = args[0]
              if fname.endswith('.csv'):
                  read_csv(fname)
              elif fname.endswith('.txt'):
                  read_text(fname)
              else:
                  print('not accepted')
          return wrapper
      
      def read_csv(fname):
          print('In read_csv()')
      
      def read_text(fname):
          print('In read_text()')
      
      @reader
      def read_file(fname):
          pass
      
      
      read_file('a.csv')
      read_file('a.txt')
      read_file('filename.py')
      

      输出

      In read_csv()
      In read_text()
      not accepted
      

      【讨论】:

        猜你喜欢
        • 2019-01-07
        • 2023-03-28
        • 1970-01-01
        • 2021-01-28
        • 2010-10-26
        • 2011-07-25
        • 2018-12-10
        • 2015-03-22
        • 1970-01-01
        相关资源
        最近更新 更多