问题描述
这是一个与设置属性相关的非常基本的问题,我没有找到明确的答案。 我有一个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