【问题标题】:Efficient class iteration in PythonPython中的高效类迭代
【发布时间】:2021-07-01 18:13:02
【问题描述】:

我有一个名为 StudentGrades 的 Python 类,类似于

class StudentGrades:
    def __init__(self, scores):
        self.scores = scores

    def average(self):
        return sum(self.scores) / len(self.scores)

    def check_grade(self, threshold=0.7):
        avg = self.average()
        if avg >= threshold:
            return "Accepted"
        return "Rejected"

然后我不得不多次使用check_grade方法,所以我会像使用它

jhon_grade = StudentGrades(scores=[0.8, 0.9])
ana_grade = StudentGrades(scores=[0.6, 0.2])

print(jhon_grade.check_grade()) # Accepted
print(ana_grade.check_grade()) # Rejected
print(jhon_grade.check_grade(0.9)) # Rejected
print(ana_grade.check_grade(0.9)) # Rejected

有没有办法我可以修改这个类来计算 jhon 和 ana 的成绩,方法是避免独立初始化类(或进行 foo 循环/列表理解),同时检查几个阈值,比如有

students = StudentGrades(scores = [[0.8, 0.9], [0.6, 0.2]])
grades = students.check_grades(threshold = [0.7, 0.9])
# returns 
[['Accepted', 'Rejected'] # Jhon (result of threshold 1, 2)
 ['Rejected', 'Rejected']] # Ana (result of threshold 1, 2)

编辑: 我希望我的基本方法具有类继承之类的东西,然后使用一些多处理来进行每个计算,但我不确定如何设置它

提前致谢!

【问题讨论】:

    标签: python python-class


    【解决方案1】:

    使用列表推导式。

    grades = [StudentGrades(scores = s).check_grade() for s in [[0.8, 0.9], [0.6, 0.2]]]
    

    【讨论】:

      【解决方案2】:

      试试这个:

      students = [[0.8, 0.9], [0.6, 0.2]]
      grades = [StudentGrades(scores).check_grade() for scores in students]
      

      输出:

      ['Accepted', 'Rejected']
      

      【讨论】:

      • 感谢您的回答,不知何故这是我试图避免的,让“用户”进行循环,因为在示例中我只放了 1 个参数,但我真正的班级需要在大参数集的网格中迭代
      【解决方案3】:

      试试这个:

      class StudentGrades:
          def __init__(self, scores: list, thresholds: list):
              self.scores = scores
              self.thresholds = thresholds
      
          def average(self, score):
              return sum(score)/len(score)
      
          def check_grade(self, scores, threshold=0.7):
              avg = self.average(scores)
              if avg >= threshold:
                  return "Accepted"
              return "Rejected"
      
          def result(self):
              return [[self.check_grade(score, threshold) for threshold in self.thresholds] for score in self.scores]
              # you could replace the return for a yield here too
      
      
      students = StudentGrades(scores=[[0.8, 0.9], [0.6, 0.2]], thresholds=[0.7, 0.9])
      grades = students.result()
      # returns [['Accepted', 'Rejected'], ['Rejected', 'Rejected']]
      

      它并不优雅(对我而言),但它使用与您已经编写的代码类似的代码并返回您想要的内容。我所做的只是添加了一个函数result,它根据每个阈值返回一个数组数组,其中包含每个学生的检查成绩。初始化类时,传入两个数组,分数数组和阈值数组。

      【讨论】:

      • scores=[[random.randrange(0,100)/100 for __ in range(100)] for _ in range(6000)];thresholds=[random.randrange(0,100)/100 for _ in range(6000)];students=StudentGrades(scores=scores, thresholds=thresholds) 适用于很多分数!
      【解决方案4】:

      我在这里读到了两个问题:

      1. 如何处理多个学生和阈值?
      2. 我怎样才能做到这一点?

      我将专注于“我如何快速完成此操作”的答案,因为三个答案已经提到如何处理多个学生。


      这里有两个步骤:

      1. Profile your code 查找花费最多的时间。轶事:通常有一小部分紧密循环,您的程序在其中花费了大部分运行时间。我在这里跳过了这一步,但对于更复杂的示例,这是必需的。
      2. Benchmark snippets 并根据需要优化,甚至重写 lower-level language

      这是您的代码版本 + @nbrix 的答案。我已经修改了check_grade 方法以返回TrueFalse 以与下面显示的numpy 版本保持一致。

      class StudentGrades:
          def __init__(self, scores):
              self.scores = scores
      
          def average(self):
              return sum(self.scores) / len(self.scores)
      
          def check_grade(self, threshold=0.7):
              avg = self.average()
              if avg >= threshold:
                  return True
              return False
      
      def run_student_grades(data):
          return [StudentGrades(scores).check_grade() for scores in data]
      

      这是我使用numpy 编写的一个函数,它计算平均值以及平均值是否大于0.7 阈值:

      import numpy as np
      
      def run_numpy_student_grades(data):
          return np.mean(data, axis=1) > 0.7
      

      对于少量输入(两个学生,两个作业),它们之间可能没有区别。事实上,使用numpy 稍微

      - benchmark 'Small Input: Two Students, Two Assignments': 2 tests -
      Name (time in us)                  Mean            Median          
      -------------------------------------------------------------------
      test_pure_python_small_input     4.9058 (1.0)      4.9330 (1.0)    
      test_numpy_small_input           8.5494 (1.74)     8.5580 (1.73)   
      -------------------------------------------------------------------
      

      对于大量输入(此处:1000 名学生,每人有 100 份作业),两者之间的差异很大:numpy 版本比初始化对象和列表的 Python 版本快约 250 倍理解他们。

      ------ benchmark 'Big Input: 1000 Students, 100 Assignments': 2 tests -----
      Name (time in us)                     Mean                 Median          
      ---------------------------------------------------------------------------
      test_numpy_big_input               55.5528 (1.0)          56.1480 (1.0)    
      test_pure_python_big_input     13,675.3789 (246.17)   13,865.2100 (246.94) 
      ---------------------------------------------------------------------------
      

      哪个版本在实践中是正确的取决于您的数据和其他外部因素:例如您将实际与多少学生和作业一起工作。


      这里是基准代码,假设实现了run_* 方法:

      # File: `benchmark.py`
      # Install: `pip install pytest pytest-benchmark numpy`
      # Run with: `pytest benchmark.py`
      
      import pytest
      from demo_plain import run_student_grades
      from demo_numpy import run_numpy_student_grades
      import numpy as np
      from numpy.random import default_rng
      
      rng = default_rng(42)
      two_students_two_assignments = np.array([[0.8, 0.9], [0.6, 0.2]])
      thousand_students_hundred_assignments = rng.standard_normal(size=(1000, 100))
      
      
      @pytest.mark.benchmark(group="Small Input: Two Students, Two Assignments")
      def test_pure_python_small_input(benchmark):
          result = benchmark(run_student_grades, two_students_two_assignments)
      
      @pytest.mark.benchmark(group="Small Input: Two Students, Two Assignments")
      def test_numpy_small_input(benchmark):
          result = benchmark(run_numpy_student_grades, two_students_two_assignments)
      
      @pytest.mark.benchmark(group="Big Input: 1000 Students, 100 Assignments")
      def test_pure_python_big_input(benchmark):
          result = benchmark(run_student_grades, thousand_students_hundred_assignments)
      
      @pytest.mark.benchmark(group="Big Input: 1000 Students, 100 Assignments")
      def test_numpy_big_input(benchmark):
          result = benchmark(run_numpy_student_grades, thousand_students_hundred_assignments)
      

      它是分开的,但这里有一个处理多个阈值的版本:

      def run_numpy_student_grades_thresholds(data, thresholds):
          _avg = np.mean(data, axis=1)
          return np.c_[
              [_avg > threshold for threshold in thresholds]
          ]
      
      print(run_numpy_student_grades_thresholds([[0.8, 0.9], [0.6, 0.2]], [0.7, 0.9]))
      # [[ True False]
      #  [False False]]
      

      【讨论】:

        猜你喜欢
        • 2013-08-05
        • 1970-01-01
        • 2015-06-16
        • 1970-01-01
        • 2013-02-14
        • 2014-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多