Python中的自变量与类变量

Python中的自变量与类变量,python,Python,你能在下面的课程中澄清周长变量吗 我知道self.vertices是针对特定实例的。既然周长没有用self定义,这是否意味着它是一个类变量?那么,不是所有的实例都有这种情况吗 将周长编码为self.permiture,这样就可以恰当地向每个实例声明周长,这难道不是正确的方法吗 这段代码来自一本书 多边形.py 新型 import math class Point: def __init__(self, x, y): self.x = x self.y =

你能在下面的课程中澄清周长变量吗

我知道
self.vertices
是针对特定实例的。既然
周长
没有用
self
定义,这是否意味着它是一个类变量?那么,不是所有的实例都有这种情况吗

将周长编码为
self.permiture
,这样就可以恰当地向每个实例声明周长,这难道不是正确的方法吗

这段代码来自一本书

多边形.py 新型

import math
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def distance(self, p2):
        return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)

class Polygon:
        def __init__(self):
            self.vertices = []
        def add_point(self, point):
            self.vertices.append((point))
        def perimetermethod(self):
            self.perimeter = 0
            points = self.vertices + [self.vertices[0]]
            for i in range(len(self.vertices)):
                self.perimeter += points[i].distance(points[i+1])
            return self.perimeter

if __name__=='__main__':
    p1 = Polygon()
    p1.add_point(Point(1,1))
    p1.add_point(Point(1,2))
    p1.add_point(Point(2,2))
    p1.add_point(Point(2,1))
    print(p1.perimetermethod())

在您的代码中,有两个东西称为
周长
。一个是
Polygon
类上的方法。另一个是该方法内部的局部变量。代码中没有类属性。

不,这意味着它是本地的。另外,您不希望使用
self.permiture
,因为它将使用相同的名称对方法进行阴影处理。

使用
some_name=…
分配一个新变量总是在最里面的封闭范围内创建变量(除非
全局
非局部
起作用,但它们在这里不相关)。在对象上指定新属性名称将在该对象上创建属性

因此
self.foo=1
在当前由
self
引用的对象上分配一个名为
foo
的属性。通常,名称
self
用作方法的第一个参数,该方法接收调用该方法的对象。所以“用self定义变量”并不是什么特别的事情;这只是分配给现有对象属性的一般规则。实例对象本身中存在的任何属性显然都必须特定于该实例

permiture=0
类的
permiture
方法内部在最内层的封闭范围内创建变量。这是
周长
方法,因此它创建了一个局部变量。局部变量只在函数调用期间存在,因此它既不是类变量,也不是实例变量。您不能从任何地方访问它,除非在该特定方法的范围内(并且它在每次调用时都有一个新的完全独立的值),因此它不能对所有实例都通用。但也不能在每个特定实例上访问不同的值,因此它也不是实例变量

如果在类块本身的方法外部有
permiture=0
,则最内部的封闭范围将是类块这将创建一个“类变量”,它只是类对象上的一个属性。如果一个属性在一个类上,那么显然它不能特定于任何实例,因为只有一个类,但可以有任意数量的实例。另一方面,这正是
多边形
类的
初始化
添加点
周长
方法的内容;它们在类块中被赋值(使用
def
语句),因此它们成为类对象的属性


总结:

  • self.foo=1
    正在分配给当前由
    self
    引用的对象上的属性(这通常是“当前实例”)
  • 类块中的
    foo=1
    正在创建所定义类的类属性
  • def块中的
    foo=1
    正在创建所定义函数的局部变量
  • 但你不应该这样记住它。它们只是以下情况的特例:

  • 分配给点名称(如
    foo.bar.baz=1
    )就是写入对象的属性
  • 分配给一个简单的名称,如
    foo=1
    ,就是在最里面的封闭范围内写入一个变量

  • 我已经使用self.permiture进行了编码,上面两种类型有什么不同…哪种方法是正确的?拥有一个同名的方法和变量只是自找麻烦,因为你(和解释器)很难理解你所指的是什么。我强烈建议您更改其中一个。我已经使用self.permiture进行了编码,上面两种类型有什么区别……哪种方法是正确的?@user1050619:为什么要将它放在对象上?这有什么用,特别是每次计算都是新的吗?谢谢Ben。这是一个很好的解释。还有一个问题。哪一个是计算周长的正确方法。将它作为实例变量(self.period)或函数变量(period)…它真的重要吗?一般来说,这取决于你的意图。在本例中,在计算方法的返回值时,您似乎将其用作临时变量,因此将其作为实例变量是很奇怪的。另外,它与方法具有相同的名称,因此如果您将其指定为对象的属性,则在调用一次方法后,您将无法再次调用该方法,因为
    square.permiture
    将获得属性
    permiture
    ,而不是方法,然后使用
    square.permiture()调用它
    会给您一个错误。@user1050619:要添加一些稍微强一点的建议,局部变量和实例变量确实很重要。他们做完全不同的事情。如果一个局部变量可以满足您的要求,那通常就是您应该使用的。使用局部变量要容易得多,因为您只需查看一个函数的代码,就可以了解如何使用它。实例变量可以从其他方法访问,甚至可以完全从类外部访问。@user1050619我还希望实例变量是实例“定义”的一部分;确定此特定实例所需的数据。周长听起来不像是特定多边形定义的一部分;相反,它应该根据定义多边形的信息(多边形列表)进行计算
    >>> square = Polygon() 
    >>> square.add_point(Point(1,1)) 
    >>> square.add_point(Point(1,2)) 
    >>> square.add_point(Point(2,2)) 
    >>> square.add_point(Point(2,1)) 
    >>> square.perimeter() 
    4.0 
    
    import math
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
        def distance(self, p2):
            return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
    
    class Polygon:
            def __init__(self):
                self.vertices = []
            def add_point(self, point):
                self.vertices.append((point))
            def perimetermethod(self):
                self.perimeter = 0
                points = self.vertices + [self.vertices[0]]
                for i in range(len(self.vertices)):
                    self.perimeter += points[i].distance(points[i+1])
                return self.perimeter
    
    if __name__=='__main__':
        p1 = Polygon()
        p1.add_point(Point(1,1))
        p1.add_point(Point(1,2))
        p1.add_point(Point(2,2))
        p1.add_point(Point(2,1))
        print(p1.perimetermethod())