【问题标题】:Missing class attribute when calling other method in class在类中调用其他方法时缺少类属性
【发布时间】:2018-11-25 18:10:37
【问题描述】:

我是所有这些东西的新手,所以请放轻松!

我写了一个类来计算各种向量结果。几个方法调用类中的其他方法来构造结果。除了一个特殊的问题外,大部分都可以正常工作。当我从另一种方法调用一个方法时,该方法的属性以某种方式被删除或丢失,我收到错误:AttributeError:'list' object has no attribute 'dot_prod',即使方法'dot_prod'是在类中定义的.我发现解决此问题的唯一方法是使用原始方法调用的返回结果建立对象的新实例。在我的代码中,我包含了问题代码和通过注释开关的解决方法,以及上下文中的 cmets 以尝试解释问题。

from math import sqrt, acos, pi

class Vector:

def __init__(self, coordinates):
    try:
        if not coordinates:
            raise ValueError
        self.coordinates = tuple([x for x in coordinates])
        self.dimension = len(coordinates)

    except ValueError:
        raise ValueError('The coordinates must be nonempty')

    except TypeError:
        raise TypeError('The coordinates must be an iterable')


def scalar_mult(self, c):
    new_coordinates = [c*x for x in self.coordinates]
    return new_coordinates


def magnitude(self):
    coord_squared = [x**2 for x in self.coordinates]
    return sqrt(sum(coord_squared))


def normalize(self):
    try:
        mag = self.magnitude()
        norm = self.scalar_mult(1.0/mag)
        return norm

    except ZeroDivisionError:
        return 'Divide by zero error'


def dot_prod(self, v):
    return sum([x*y for x,y in zip(self.coordinates, v.coordinates)])


def angle(self, v):

## This section below is identical to an instructor example using normalized unit
## vectors but it does not work, error indication is 'dot_prod' not
## valid attribute for list object as verified by print(dir(u1)). Instructor is using v2.7, I'm using v3.6.2.
## Performing the self.normalize and v.normalize calls removes the dot_prod and other methods from the return.
## My solution was to create new instances of Vector class object on  self.normalize and v.normalize as shown below:
##        u1 = self.normalize()    # Non working case
##        u2 = v.normalize()       # Non working case
    u1 = Vector(self.normalize())
    u2 = Vector(v.normalize())

    unit_dotprod = round((u1.dot_prod(u2)), 8)

    print('Unit dot product:', unit_dotprod)

    angle = acos(unit_dotprod)
    return angle

#### Test Code #####

v1 = Vector([-7.579, -7.88])
v2 = Vector([22.737, 23.64])


print('Magnitude v1:', v1.magnitude())
print('Normalized v1:', v1.normalize())
print()

print('Magnitude v2:', v2.magnitude())
print('Normalized v2:', v2.normalize())
print()
print('Dot product:', v1.dot_prod(v2))
print('Angle_rad:', v1.angle(v2))

据我所知,方法'angle(self,v)'是问题所在,代码中的注释说明了更多。变量 u1 和 u2 有一个注释开关可以在它们之间切换,您会看到在工作案例中,我创建了 Vector 对象的新实例。我根本不知道原始方法调用中缺少属性的原因是什么。调用 u1.dot_prod(u2) 时的下一行是追溯错误的体现,在非工作情况下通过执行 dir(u1) 验证的属性中缺少“dot_prod”。

欣赏人们在这里的见解。我不太了解技术术语,所以希望我能跟上。

【问题讨论】:

    标签: python class-method class-attributes


    【解决方案1】:

    您试图将列表而不是 Vector 传递给您的 dot_prod 方法(在命令 u2 = v.normalize();该方法的返回对象是一个列表)。我认为您的问题是您假设 u2 将作为属性附加到该类,但您必须调用 self 作为某个点来执行此操作。调用方法并将输出重新附加为属性有两种正确的方法:

    (1) 您可以在类被实例化(创建)后调用它,如下所示:

        vec = Vector([-7.579, -7.88])
        vec.normal_coords = vec.normalize()
    

    如果您不希望对每个 Vector 实例都执行此操作,并且您不需要在许多其他方法中使用该属性,则此方法效果更好。由于您需要归一化坐标来查找角度,因此我建议您:

    (2) 在实例化期间作为属性附加(下面的长代码,以充分展示其工作原理):

    from math import sqrt, acos, pi
    
    class Vector(object):
    
        def __init__(self, coordinates):
            try:
                if not coordinates:
                    raise ValueError
                self.coordinates = tuple([x for x in coordinates])
                self.dimension = len(coordinates)
    
                # Next line is what you want - and it works, even though
                # it *looks like* you haven't defined normalize() yet :)
                self.normalized = self.normalize()
    
            except ValueError:
                raise ValueError('The coordinates must be nonempty')
    
            except TypeError:
                raise TypeError('The coordinates must be an iterable')
    
       [...]
    
        def dot_prod(self, v):
            # v is class Vector here and in the angle() method
            return sum([x*y for x,y in zip(self.normalized, v.normalized)])
    
    
        def angle(self, v):
            unit_dotprod = round((self.dot_prod(v)), 8)
    
            print('Unit dot product:', unit_dotprod)
            angle = acos(unit_dotprod)
    
            return angle
    

    【讨论】:

    • 感谢 cmets。我将列表更改为 .normalize 函数调用中的元组。所以现在 dot_prod 方法传递的是元组而不是列表,但问题是 u1 和 u2 = v.normalize() 调用中仍然缺少 'dot_prod' 属性。您对 dot_prod 所做的更改调用“有效”,因为代码不会崩溃,但数学运算已关闭,这就是您看到 ValueError 的原因,因为点积返回了错误的值。 v.normalize 缩放矢量,但不幸的是,对象中仍然缺少“dot_prob”属性。试试 dir(u2)。
    • 啊,是的,你是绝对正确的 - 在我匆忙中我没有注意到你想要将归一化坐标传递给点积以获得角度。自从我处理向量以来已经有一段时间了 :) 简短的回答是你有点试图附加 normalize 作为方法和外部函数和类并不能真正像那样工作(尽管你 can 在初始化中调用方法并将答案重新附加为属性,我认为这可能是您真正想要的)。当我有更多时间时,我会更新我的答案
    • @Bill,请按照建议更改缩进 - 否则您问题中的代码无效。
    • 关于缩进,在我的 IDE 中一切都很好,但是当我将它粘贴到这里时,格式很不稳定,所以我可以让它在代码块中干净地发布的唯一方法是删除def 缩进。在粘贴之前,我按照说明缩进了 4 个空格,但这并不好。我以为我可以干净地复制和粘贴,但由于某种原因它不干净。如果我在此过程中遗漏了什么,请告诉我,谢谢。
    • @HFBrowning,感谢您的宝贵反馈。我做了你提议的编辑,它更接近于需要的东西。额外的更改是将 dot_prod() 复制到 dot_prod_unit() ,这是角度函数所需的特殊情况。请参阅上面的编辑。
    猜你喜欢
    • 1970-01-01
    • 2017-02-14
    • 2017-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-19
    相关资源
    最近更新 更多