【问题标题】:Django Create Class From Multiple FunctionsDjango 从多个函数创建类
【发布时间】:2020-06-01 07:04:04
【问题描述】:

我的 utils.py 文件中有几个函数,我的视图使用这些函数来计算交易统计数据。下面我只添加了 3 个,但有 8 个,很快就会有更多。我目前设置它的方式有效,但效率非常低。你可以看到每个函数都是从头开始调用其他设置类似变量的函数。

把它变成一个类的正确方法是什么?

这是正确的方法吗?


utils.py |多种功能

def max_amount_function (pk):
    trade = get_object_or_404(Trade, pk=pk)
    entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")
    max_amount_function = entries_buy.aggregate(max_amount_function=Sum('amount'))['max_amount_function']
    if max_amount_function is None:
        return 0
    else:
        return max_amount_function

def fees_function (pk):
    trade = get_object_or_404(Trade, pk=pk)
    entries = Entry.objects.filter(trade=trade)
    fees_function = entries.aggregate(fees_function=Sum('fee'))['fees_function']
    return fees_function

def entry_cpu_function (pk):
    if max_amount_function(pk) > 0:
        trade = get_object_or_404(Trade, pk=pk)
        entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")
        entry_cpu_function = entries_buy.annotate(
            s=F('amount') * F('price')
        ).aggregate(
            entry_cpu_function=ExpressionWrapper(
                Sum(
                    Cast('s', output_field=models.FloatField())
                ) / Sum('amount'),
                output_field=models.FloatField()
            )
        )['entry_cpu_function']
        return entry_cpu_function
    else:
        return 0

utils.py |一节课

class TradeStats:
    trade = get_object_or_404(Trade)
    entries_buy = Entry.objects.filter(trade=trade, entry_type="entry")

    def __init__(self):
        pass

    def max_amount_functio(pk):
        pass

    def fees_function(pk):
        pass

    def entry_cpu_function(self):
        pass

编辑添加了 models.py,因为它可能与条目相关,因为它是一个不同的类。

模型.py

class Trade(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
    status = models.CharField(max_length=2, choices=STATUS_CHOICES, default='op')
    broker = models.ForeignKey(Broker, on_delete=models.CASCADE, blank=True, null=True)
    asset = models.ForeignKey(Asset, on_delete=models.CASCADE, null=True)
    ...

class Entry(models.Model):

    ENTRY = 'entry'
    EXIT = 'exit'
    ENTRY_TYPE_CHOICES = [
        (ENTRY, 'Entry'),
        (EXIT, 'Exit'),

    trade = models.ForeignKey(Trade, on_delete=models.CASCADE)
    amount = models.FloatField(null=True)
    price = models.FloatField(null=True, blank=True)
    entry_type = models.CharField(max_length=5, choices=ENTRY_TYPE_CHOICES, default=ENTRY)
    ...

【问题讨论】:

    标签: python django function class methods


    【解决方案1】:

    您可以将所有这些函数移动到模型或自定义 QuerySet 下。

    如果你转向模型,你可以做类似的事情。 (代码可能行不通,仅供参考)

    # MODEL
    class Trade(models.Model):
    
        # Model fields here
    
        def get_entries(self):
            # NB: assuming you didn't specify a related_name
            # in the `Entry.trade` ForeignKey field. Else,
            # replace `entry_set` with said related_name.
            return self.entry_set.filter(entry_type="entry")
    
    
        def max_amount_function(self):
            max_amount_res = self.get_entries().aggregate(max_amount_function=Sum('amount'))['max_amount_function']
            if max_amount_res is None:
                return 0
            else:
                return max_amount_res
    
        def fees_function(self):
            return self.get_entries().aggregate(fees_function=Sum('fee'))['fees_function']
    
        def entry_cpu_function(self, ):
    
            if self.max_amount_function() <= 0:
                return 0
    
            entry_cpu_function = self.get_entries().annotate(
                s=F('amount') * F('price')
            ).aggregate(
                entry_cpu_function=ExpressionWrapper(
                    Sum(
                        Cast('s', output_field=models.FloatField())
                    ) / Sum('amount'),
                    output_field=models.FloatField()
                )
            )['entry_cpu_function']
            return entry_cpu_function
    

    在views.py中,你可以像下面这样引用这个函数 -

    trade = get_object_or_404(Trade, pk=pk)
    trade.max_amount_function()
    trade.fees_function()
    trade.entry_cpu_function()
    

    【讨论】:

    • self.trades.filter (在get_entries 中)肯定是错误的,除非OP 将Entry.trade 中的“related_name”定义为“交易”(这将是一个错误的命名开始)。由于我们没有Trade 模型定义,最好假设“related_name”没有设置并使用默认值,所以这应该是self.entry_set.filter
    • 此外,虽然在技术上没有错,但方法定义中self 之后的逗号是无用的,可能会误导新手,而且完全不符合 Python 标准。
    • 同意这两点,布鲁诺。我已经通过考虑相关名称集来回答。感谢您的编辑。
    • 感谢您迄今为止的大力帮助。真的很酷的方法。我将models.py添加到我的问题中,希望能解决'entry_set'评论。仍在努力实施。
    • 谢谢亚历克斯!如果对您有帮助,请不要忘记接受答案。
    【解决方案2】:

    Rockstar 已经发布了在这种情况下的最佳解决方案 - 您正在使用 Django 模型,并且您的所有代码都依赖于特定的模型实例,因此将这些功能作为方法添加到您的 Trade 模型中是非常明显的解决方案。

    话虽如此,这个问题本身(如何将一堆依赖于相同变量的函数变成一个类)也可以用更通用的答案来解决,该答案也适用于其他上下文:

    首先,您要修复此类定义,因此 tradesentries_buyinstance 属性,而不是类属性 - 否则它们将在模块导入时仅初始化一次,您将结束加上陈旧的数据。为此,您还需要将 pk 参数添加到您的类初始化程序中:

    class TradeStats:
        def __init__(self, pk):
            self.trade = get_object_or_404(Trade, pk=pk)
            self.entries_buy = Entry.objects.filter(trade=self.trade, entry_type="entry")
    

    然后您想将self 作为第一个参数添加到您的所有方法(否则当前实例无法传递 - 并删除现在无用的 'pk' 参数:

    def max_amount_function(self):
        pass
    
    def fees_function(self):
        pass
    
    def entry_cpu_function(self):
        pass
    

    现在您可以向后移植您的方法实现,将相关部分替换为对self.tradeself.entries_buy 的访问,即:

    def max_amount_function(self):
        result = self.entries_buy.aggregate(max_amount_function=Sum('amount'))['max_amount_function']
        return 0 if result is None else result
    

    等等……

    此外,“xxx_yyy_function”是一个非常糟糕的命名。作为一般规则,您希望对对象使用名词(变量、值、类名等),对函数或方法使用动词,所以人们会期望像 get_max_amountget_fees 等这样的东西。

    最后,鉴于您的问题(以及您第二次 sn-p 的样子),我强烈建议您花一些时间在 the official Python tutorial - the part about classes 当然,但您可能会学到很多其他有用的东西教程。

    【讨论】:

    • 感谢您的详细回答!我知道我有很多工作要做,而且我一直在阅读关于类的 Python 教程,但在我的情况下仍然很难想象它。我很难决定解决方案是像您的示例一样是独立类还是在我的模型内部(这是我从未考虑过的),例如您似乎也同意的 Rockstar 示例是最好的方法。将来,将从这些函数中编译出大量统计数据,因此最有效的将是理想的。
    • @AlexWinkler 模型不仅与数据访问有关,而且还与域逻辑有关,因此选择“智能模型”(而不是将这种逻辑分散在视图/实用程序函数等中)是有意义的。对于您给出的示例(每个实例的统计信息),模型是最明显的“主机”。对于“每个子集”的统计数据(一组贸易对象的统计数据,即每年等),ModelManager 显然是主机。现在,如果您有很多基于六个模型等的复杂统计数据,您可能确实需要考虑不同的专用类。
    • wrt/ 效率 - 至少对于整个数据集的分析计算 - 您可能希望完全避免加载模型实例并利用您的数据库计算能力。实际上,您甚至可能想要数据的替代表示,参见 OLAP vs OLTP
    • 看来我得到了接下来几个周末的阅读材料哈哈感谢布鲁诺的所有帮助。对此,我真的非常感激。 (我还更新了函数名称?)
    猜你喜欢
    • 2014-06-21
    • 2018-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多