简体   繁体   English

用户输入未传递给__init__

[英]User input isn't passed to __init__

I am trying to create a polygon class that returns the area and perimeter when given the sides and length from user input. 我试图创建一个多边形类,当根据用户输入给出边和长度时,该类返回面积和周长。 However, it doesn't take the two variables I'm trying to pass into the __init__ method. 但是,它并不需要我试图传递给__init__方法的两个变量。 The sides and length must be private and must be received through user input. 边和长度必须是私有的,并且必须通过用户输入来接收。

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()

You seem to have a fundamental misunderstanding of how OOP works in Python. 您似乎对OOP在Python中的工作方式有基本的误解。 When you instantiate a class, the __init__() method is called, and usually you assign the arguments given to instance variables, like so: 实例化一个类时,将调用__init__()方法,通常将给定的参数分配给实例变量,如下所示:

class Pet(object):

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

Then in whatever methods you'd like to make use of those, you can access them through the instance: 然后,无论您想使用哪种方法,都可以通过实例进行访问:

def get_name(self):
    return self._name

Notice that all this method does is return self._name back to the caller. 注意,此方法所做的只是将self._name返回给调用者。 There's a common idiom for this situation using decorators : 使用装饰器可以解决这种情况:

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

The advantage of this compared to get_name() is twofold. get_name()相比,此方法有两个优点。 First, you can call the method without parentheses as if it was an instance variable: 首先,您可以在没有括号的情况下调用该方法,就好像它是一个实例变量一样:

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

>> Rosita

Secondly, if the user later tried to overwrite it with something else Python would raise an AttributeError: 其次,如果用户以后尝试用其他东西覆盖它,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

In regards to your __repr__ method, what I think you meant was this: 关于您的__repr__方法,我认为您的意思是:

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

__repr__ is called when you do print(my_polygon) or str(my_polygon) so it should return a string. __repr__当你被称为print(my_polygon)str(my_polygon)所以应该返回一个字符串。

Finally, you may have noticed I have named the instance variables with one leading underscore rather than two. 最后,您可能已经注意到,我用一个下划线而不是两个下划线命名了实例变量。 If you want the users of your class to know that a particular instance variable is "private" and they should not mess with it, it's better to prefix its name with only one underscore. 如果您希望类的用户知道一个特定的实例变量是“私有”的,并且他们不应该将其弄乱,那么最好仅在其下划线加一个下划线。 The reason for that is that it allows you to have acessor methods and instance variables with the same name, while at the same time avoiding name mangling . 这样做的原因是,它允许您使用具有相同名称的acessor方法和实例变量,同时避免名称修改 Names with two leading underscores are mangled, and thus generally not recommended. 带有两个下划线的名称会被修饰,因此通常不建议使用。

Taking all of this into account, here's a rewrite of your code: 考虑到所有这些,这是您的代码的重写:

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)

Your problem is that you're not passing anything to __init__ , you're creating class level variables when you do this: 您的问题是,您没有将任何东西传递给__init__ ,而是在执行以下操作时创建了类级变量:

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

These will only be called once per program* as well, so you'll never be able to provide different values, if that's something you need. 每个程序也只会调用一次这些*,因此,如果您需要这样做,您将永远无法提供不同的值。 If you want to pass arguments to a function, you do that when you create the instance of the class: 如果要将参数传递给函数,请在创建类的实例时执行此操作:

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

As others have mentioned, private variables in Python aren't actually inaccessible, though there are ways to enforce that they are immutable, as jonrsharpe pointed out it his answer. 正如其他人提到的那样,Python中的私有变量实际上并不是不可访问的,尽管有一些方法可以使它们成为不可变的,正如jonrsharpe指出的那样。 You could also investigate __slots__ for other options. 您还可以调查__slots__以获得其他选择。

* Per import, which is usually once per program, but there are ways you can get around that, but that's even worse. * 每次导入, 通常每个程序一次,但是有很多方法可以解决这个问题,但这甚至更糟。 Unless you're going for an obfuscated Python contest, don't do that. 除非您打算参加一场混乱的Python竞赛,否则不要这样做。

Here is a quick code review of your code (no math consideration): 这是您的代码的快速代码回顾(不考虑数学):

You can inherit from object (to be compatible with Python 2): 您可以从object继承(与Python 2兼容):

class Polygon(object):

Simplify the parameters names, don't use double underscores: 简化参数名称,不要使用双下划线:

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

Use the instance variable self.sides and self.length , drop the parameters: 使用实例变量self.sidesself.length ,删除参数:

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

Replace tan() by math.tan() math.tan()替换tan() math.tan()

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

In your main() function: 在您的main()函数中:

(With Python 2, use raw_input instead of input .) (对于Python 2,请使用raw_input而不是input 。)

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

Convert string values to int 将字符串值转换为int

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

p1 = Polygon(sides, length)

Use print() function to print the result 使用print()函数打印结果

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

Here is how I would write this: 这是我的写法:

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(',')))

Why? 为什么? Because: 因为:

  • inheriting from a namedtuple makes the instance immutable, so you can't reassign sides and length after initial creation, while giving you sensible equality comparison and __repr__ formatting for free: 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 
  • Using @property means you can easily access the calculated attributes, again in a read-only way: 使用@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 
  • Using @classmethod keeps the logic for creating an object from user input within the class, where it belongs: 使用@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 

    In your current implementation, your input runs once when the class is defined, not when the user actually wants to create an instance. 在当前的实现中,您的输入在定义类时运行一次,而不是在用户实际要创建实例时运行。

Note : I've assumed you're using Python 3.x - if not you should use raw_input . 注意 :我假设您使用的是Python 3.x-如果没有,则应使用raw_input Also your classes should inherit from object , when you're not using eg namedtuple . 当不使用namedtuple时,您的类也应该从object继承。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM