簡體   English   中英

用戶輸入未傳遞給__init__

[英]User input isn't passed to __init__

我試圖創建一個多邊形類,當根據用戶輸入給出邊和長度時,該類返回面積和周長。 但是,它並不需要我試圖傳遞給__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()

您似乎對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返回給調用者。 使用裝飾器可以解決這種情況:

@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方法和實例變量,同時避免名稱修改 帶有兩個下划線的名稱會被修飾,因此通常不建議使用。

考慮到所有這些,這是您的代碼的重寫:

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)

您的問題是,您沒有將任何東西傳遞給__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競賽,否則不要這樣做。

這是您的代碼的快速代碼回顧(不考慮數學):

您可以從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

math.tan()替換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())

這是我的寫法:

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 當不使用namedtuple時,您的類也應該從object繼承。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM