用户输入未传递给__init__

问题描述 投票:0回答:4

我正在尝试创建一个多边形类,当给定用户输入的边和长度时,它返回区域和周长。但是,它没有采用我试图传递给__init__方法的两个变量。边和长度必须是私人的,必须通过用户输入接收。

import math

class Polygon:
    use = (input("Enter in the sides and length of sides seperated by a comma"))
    ans = use.split(",")
    __numofSides = ans[0]
    __sideLength = ans[1]

    def __init__(self, __numofSides, __sideLength):
        self.__numofSides = __numofSides
        self.__sideLength = __sideLength

    def get__numofSides(self):
        self.__numofSides = ans[0]
        return __numofSides

    def get__sideLength(self):
        self.__sideLength = ans[1]
        return __sideLength

    def perimeter(self, __numofSides,__sideLength):
        peri = self. __numofSides * self.__sideLength
        return peri

    def area(self, __numofSides, __sideLength):
        area = (((__sideLength **2) * __numofSides) / (tan(4) *(math.pi/__numofSides))) 
        return area

    def __str___(self,):
        print("Number of Sides: {}\n Length of Sides: {}\n" \
              "Perimeter is: {}\n Area is: {}".format(__numofSides,__sideLength,peri,area))

def main():
    p1 = Polygon()
    p1.perimeter()
    p1.area()
    p1.__str__()

main()
python variables init
4个回答
2
投票

你的问题是你没有向__init__传递任何内容,当你这样做时你正在创建类级变量:

class Foo:
    x = 42
    y = input("Don't do this, this is bad")

这些只会在每个程序*中调用一次,因此如果您需要,您将永远无法提供不同的值。如果要将参数传递给函数,则在创建类的实例时执行此操作:

class Polygon:
    def __init__(self, num_of_sides, side_length):
        self._num_of_sides = num_of_sides
        self._side_length = side_length

正如其他人所提到的,Python中的私有变量实际上并不可访问,尽管有一些方法可以强制它们是不可变的,因为jonrsharpe指出了他的答案。您还可以调查__slots__的其他选项。

*每次导入,通常每个程序一次,但有一些方法可以绕过它,但更糟糕的是。除非您要参加混淆的Python比赛,否则请不要这样做。


2
投票

您似乎对OOP如何在Python中工作有一个基本的误解。实例化一个类时,会调用__init__()方法,通常将赋值给实例变量,如下所示:

class Pet(object):

    def __init__(self, name):
        self._name = name # argument stored in self._name

然后,无论您想使用哪种方法,都可以通过实例访问它们:

def get_name(self):
    return self._name

请注意,所有这些方法都将self._name返回给调用者。使用decorators这种情况有一个共同的习惯用法:

@property
def name(self):
    return self._name

get_name()相比,这种优势是双重的。首先,您可以调用没有括号的方法,就像它是一个实例变量一样:

my_pet = Pet('Rosita')
print(my_pet.name) 

>> Rosita

其次,如果用户后来尝试用其他东西覆盖它,Python会引发一个AttributeError:

my_pet = Pet('Rosita')
my_pet.name = 'Maggie'

>> Traceback (most recent call last):
>> File "<stdin>", line 1, in <module>
>> AttributeError: can't set attribute

关于你的__repr__方法,我认为你的意思是:

def __repr__(self):
    return "<Polygon sides={}; length={}; perimeter={}; area={}>".format(
        self.sides, self.side_length, self.perimeter, self.area)

当你做__repr__print(my_polygon)时会调用str(my_polygon),所以它应该返回一个字符串。

最后,您可能已经注意到我已使用一个前导下划线而不是两个来命名实例变量。如果您希望类的用户知道特定的实例变量是“私有的”并且他们不应该使用它,那么最好在其名称前面加上一个下划线。原因是它允许你拥有具有相同名称的acessor方法和实例变量,同时避免使用name mangling。具有两个前导下划线的名称被破坏,因此通常不建议使用。

考虑到所有这些因素,这里是对代码的重写:

import math

class RegularPolygon(object):

    def __init__(self, sides, side_length):
        self._sides = sides
        self._side_length = side_length

    @property
    def sides(self):
        return self._sides

    @property
    def side_length(self):
        return self._side_length

    @property
    def perimeter(self):
        return self.sides * self.side_length

    @property
    def area(self):
        return ((self.side_length**2 * self._sides) 
                / (4 * math.tan(math.pi / self.sides)))

    def __repr__(self):
        return "<Polygon sides={}; length={}; perimeter={}; area={}>".format(
            self.sides, self.side_length, self.perimeter, self.area)


if __name__ == '__main__':
    poly = RegularPolygon(5, 7)
    print(poly)

1
投票

以下是对代码的快速代码审查(无数学考虑):

您可以继承object(与Python 2兼容):

class Polygon(object):

简化参数名称,不要使用双下划线:

    def __init__(self, sides, length):
        self.sides = sides
        self.length = length

使用实例变量self.sidesself.length,删除参数:

    def perimeter(self):
        return self.sides * self.length

tan()替换math.tan()

    def area(self):
        return ((self.length ** 2) * self.sides) / (math.tan(4) * (math.pi / self.sides))

在你的main()功能:

(使用Python 2,使用raw_input而不是input。)

use = input("Enter in the sides and length of sides separated by a comma: ")
ans = use.split(",")

将字符串值转换为int

sides = int(ans[0])
length = int(ans[1])

p1 = Polygon(sides, length)

使用print()函数打印结果

print(p1.perimeter())
print(p1.area())

1
投票

我是这样写的:

from collections import namedtuple
from math import pi, tan

class Polygon(namedtuple('Polygon', 'sides,length')):

    PROMPT = 'Enter in the number and length of sides, separated by a comma'

    @property
    def perimeter(self):
        return self.sides * self.length

    @property
    def area(self):
        return (self.sides * (self.length ** 2)) / (4 * tan(pi / self.sides))

    @classmethod
    def from_input(cls):
        return cls(*map(int, input(cls.PROMPT).split(',')))

为什么?因为:

  • namedtuple继承使得实例不可变,所以你不能在初始创建后重新分配sideslength,同时免费提供合理的相等比较和__repr__格式: >>> square = Polygon(4, 1) >>> square Polygon(sides=4, length=1) >>> square.sides = 5 Traceback (most recent call last): File "python", line 1, in <module> AttributeError: can't set attribute
  • 使用@property意味着您可以再次以只读方式轻松访问计算的属性: >>> square.area 1.0000000000000002 >>> square.perimeter 4 >>> square.area = 7 Traceback (most recent call last): File "python", line 1, in <module> AttributeError: can't set attribute
  • 使用@classmethod保留了在类所属的用户输入中创建对象的逻辑: >>> hexagon = Polygon.from_input() Enter in the number and length of sides, separated by a comma 6,2 >>> hexagon Polygon(sides=6, length=2) >>> hexagon.area 10.392304845413264 >>> hexagon.perimeter 12 在当前的实现中,输入在定义类时运行一次,而不是在用户实际想要创建实例时运行。

注意:我假设您使用的是Python 3.x - 如果不是,您应该使用raw_input。当你不使用时,你的类应该继承objectnamedtuple

© www.soinside.com 2019 - 2024. All rights reserved.