使用类方法在 PYTHON 类中分配属性值

问题描述

这是一个与设置属性相关的非常基本的问题,我没有找到明确的答案。 我有一个python类,如下

class circle():
    
    PI = 3.14
    
    def __init__(self,radius):
        self.radius   = radius
        # 1. first way of assigning an atribute
        self.area     = self._area()
        # 2. second way of assigning an attribute (running a method)
        self._squarearea(self.radius,self.area)
        # 3. third way of assigning an attribute
        self.diameter = self._diameter(self.radius)
        
    def _area(self):
        return self.radius * self.PI * self.PI
    
    def _squarearea(self,radius,area):
        self.sqarea = radius * radius * 4 - area
        
    def _diameter(self,radius):
        return self.radius *2

    def giveme4timesradius(self):
        return 4*self.radius

我列出了 4 种分配属性或计算数据的方法。 在我的特定真实示例中,每种方法都执行复杂的计算,以便更好地保持分区以方便阅读代码

我做的每一项作业都有一定的缺点

  • 方式一:self.area = self._area()。 读者现在不会在 init 方法中计算多少变量占用的面积。运行了一个方法,但读者必须深入代码才能了解计算的内容以及如何计算

  • 方式 2:self._squarearea(self.radius,self.area) 在这种情况下,读者不知道函数的作用,即使函数设置了一个新的属性。相反,至少表明将半径和面积传递给函数。实际上,传递的两个变量只是一个指示,因为在类方法中所有类属性都可用。

  • 方式 3:self.diameter = self._diameter(self.radius) 这里读者知道半径是pass,也知道分配了一个属性(直径)

  • 方式四: 可以考虑提供最后一种方法,让类的使用者调用方法 giveme4timesradius(),但这很不方便,因为我希望所有都预先计算。并且每次调用时都必须计算它。

有多个关于属性、类和教程的 SO 问题(见下文),但我没有在任何地方看到这种比较。 我想我不需要任何 setter 和 getter,因为我只想从一开始就运行所有,根据多个条件,某些属性将使用某些类方法或其他方法计算。

那么“pythonic”方式是什么?或者其中任何一个

参考文献:
https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide
https://realpython.com/lessons/adding-attributes-python-class/
https://www.python-course.eu/python3_class_and_instance_attributes.php
https://medium.com/shecodeafrica/managing-class-attributes-in-python-c42d501c5ee0

解决方法

正如您所说,第二个选项令人困惑,因为您需要转到方法定义以了解对类实例中的新变量的赋值。从这个意义上说,第一个选项更清晰一些,但我仍然想知道该方法的必要输入参数是什么。

然而,由于两个原因,第三个选项是错误的。一,您传递了半径,但您仍然可以访问 self.radius,因此该参数是多余的。其次,即使你使用了参数,它也应该是一个用@staticmethod修饰的静态方法,因为你没有使用类实例(self)。

因此,我会使用一种静态方法,该方法利用它需要的正确输入变量。类似的东西:

class Circle:

    PI = 3.14

    def __init__(self,radius):
        self.radius = radius
        self.area = self.compute_area(self.radius)

    @staticmethod
    def compute_area(radius):
        return radius * (Circle.PI**2)
,

这是 @property 装饰器的一个很好的用例。

class Circle():
    
    PI = 3.14
    
    def __init__(self,radius):
        self.radius   = radius
    
    @property
    def area(self):
        return self.radius * (self.PI ** 2)
    
    @property
    def squarearea(self):
        return (self.radius ** 2)  * 4 - self.area
    
    @property
    def diameter(self):
        return self.radius *2
    
    @property
    def giveme4timesradius(self):
        return 4*self.radius

作为我的偏好,我会使用@dataclass:

from dataclasses import dataclass
from typing import ClassVar

@dataclass
class Circle():

    radius: float
    PI: ClassVar[float] = 3.14

    # No need __init__ -> handled by dataclass
    
    @property
    def area(self):
        return self.radius * (self.PI ** 2)
    
    @property
    def squarearea(self):
        return (self.radius ** 2)  * 4 - self.area
    
    @property
    def diameter(self):
        return self.radius *2
    
    @property
    def giveme4timesradius(self):
        return 4*self.radius