为什么在类的方法中硬编码类名被认为是不良实践?

4
在Python中,为什么这样做是不好的:

class Circle:
  pi = 3.14159 # class variable
  def __init__(self, r = 1):
    self.radius = r
  def area(self):
    return Circle.pi * squared(self.radius)

def squared(base): return pow(base, 2)

区域方法可以定义如下:
def area(self): return self.__class__.pi * squared(self.radius) 

除非我大错特错,这被认为是引用类变量的更好方式。问题是为什么?直觉上,我不喜欢它,但我似乎并不完全理解这一点。


2
区域内没有自身。 def area(self): - Versatile
1
你理解错了,你应该使用Circle.pi,而不是self.__class__.pi或者type(self).pi - Bi Rico
@Versatile 看起来我打错了,应该是 area 中有 self。 - weeCoder
@BiRico 为什么我不应该使用self.class.pi? - weeCoder
你从何得出这样一个前提,即直接引用类名是一种不好的做法? - user2357112
显示剩余2条评论
4个回答

4

因为如果子类化该类,则它将不再引用该类,而是其父类。在您的情况下,这确实没有任何区别,但在许多情况下确实有区别:

class Rectangle(object):
    name = "Rectangle"
    def print_name(self):
        print(self.__class__.name) # or print(type(self).name)

class Square(Rectangle):
    name = "Square"

如果你实例化 Square 然后调用它的 print_name 方法,它会打印出 "Square"。如果你使用 Rectangle.name 而不是 self.__class__.name(或 type(self).name),它会打印出 "Rectangle"。

值得注意的是,只要您清楚区分实例属性和类属性,甚至不需要获取对类的引用。 只要您没有在 self 上设置 name 属性,self.name 将解析为与 type(self).name 解析为相同的内容。 - mgilson
假设您希望该事物可以被覆盖,如果您希望它可以被覆盖,最好使用self.whatever而不是self.__class__.whatever。并非所有内容都应该被覆盖。 - user2357112

2
我可以在这里列举两个原因:
1. 继承 2. HTML标签
class WeirdCircle(Circle):
    pi = 4

c = WeirdCircle()
print(c.area()) 
# returning 4 with self.__class__.pi 
# and 3.14159 with Circle.pi

当您想要重命名类时,只需修改一个位置即可。


2
为什么在类的方法中硬编码类名被认为是不好的实践?
这并不是不好的实践。我不知道你为什么认为它不好。
在类的方法中硬编码类名有很多好处。例如,在Python 2中使用super:
super(ClassName, self).whatever()

人们经常试图用super(self.__class__, self).whatever()来替换它,但这是完全错误的。第一个参数必须super调用发生的实际类,而不是self.__class__,否则查找将找到错误的方法。
硬编码类名的另一个原因是为了避免覆盖。例如,假设您使用另一个方法实现了一个方法,如下所示:
class Foo(object):
    def big_complicated_calculation(self):
        return # some horrible mess
    def slightly_different_calculation(self):
        return self.big_complicated_calculation() + 2

如果你希望slightly_different_calculation不受big_complicated_calculation的覆盖影响,你可以显式地引用Foo.big_complicated_calculation

def slightly_different_calculation(self):
    return Foo.big_complicated_calculation(self) + 2

即使你想要使用覆盖,通常也最好将ClassName.whatever更改为self.whatever,而不是self.__class__.whatever


0
Python之禅说,为了使代码易读,应尽可能保持简单。为什么要使用类名或super呢?如果只使用self,则将引用相应的类并打印其相关变量。请参考下面的代码。
class Rectangle(object):
    self.name = "Rectangle"
    def print_name(self):
        print(self.name)

class Square(Rectangle):
    name = 'square'

sq = Square()
sq.print_name

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接