2015-10-01 68 views
3

我正在修改text adventure game tutorial,github,以適應python 2.7。我爲我的IDE使用了PyCharm 4.5.4社區版。當我不重寫父方法,它給了我一個錯誤:我必須在python 2.7中實現所有的抽象方法嗎?

Class WolfRoom must implement all abstract methods

首先擺脫這種錯誤,我定義缺少方法def modify_player(self, the_player):pass,但我很快就意識到我被覆蓋方法與它無關不是我想要的。現在,如果我只是從WolfRoom類中移除該方法,則會出現IDE錯誤,如上所示,但在運行我的遊戲時似乎工作得很好。我應該離開這個方法還是定義它並使用super()

下面是一些代碼片段:

class MapTile(object): 
    """The base class for all Map Tiles""" 

    def __init__(self, x, y): 
     """Creates a new tile. 
     Attributes: 
      :param x: The x coordinate of the tile. 
      :param y: The y coordinate of the tile. 
     """ 
     self.x = x 
     self.y = y 

    def intro_text(self): 
     """Information to be displayed when the player moves into this tile.""" 
     raise NotImplementedError() 

    def modify_player(self, the_player): 
     """Process actions that change the state of the player.""" 
     raise NotImplementedError() 

    def adjacent_moves(self): 
     """Returns all move actions for adjacent tiles.""" 
     moves = [] 
     if world.tile_exists(self.x + 1, self.y): 
      moves.append(actions.MoveEast()) 
     if world.tile_exists(self.x - 1, self.y): 
      moves.append(actions.MoveWest()) 
     if world.tile_exists(self.x, self.y - 1): 
      moves.append(actions.MoveNorth()) 
     if world.tile_exists(self.x, self.y + 1): 
      moves.append(actions.MoveSouth()) 
     return moves 

    def available_actions(self): 
     """Returns all of the available actions in this room""" 
     moves = self.adjacent_moves() 
     moves.append(actions.ViewInventory()) 
     return moves 

...

class EnemyRoom(MapTile): 
    def __init__(self, x, y, enemy): 
     self.enemy = enemy 
     super(EnemyRoom, self).__init__(x, y) 

    def intro_text(self): 
     pass 

    def modify_player(self, the_player): 
     if self.enemy.is_alive(): 
      the_player.hp = the_player.hp - self.enemy.damage 
      print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, the_player.hp)) 

    def available_actions(self): 
     if self.enemy.is_alive(): 
      return [actions.Flee(tile=self), actions.Attack(enemy=self.enemy)] 
     else: 
      return self.adjacent_moves() 

...

class WolfRoom(EnemyRoom): 
    def __init__(self, x, y): 
     super(WolfRoom, self).__init__(x, y, enemies.Wolf()) 

    def intro_text(self): 
     if self.enemy.is_alive(): 
      return """ 
      A grey wolf blocks your path. His lips curl to expose canines as white as 
      the nights sky. He crouches and prepares to lunge. 
      """ 
     else: 
      return""" 
      The corpse of a grey wolf lays rotting on the ground. 
      """ 
+2

Python實際上並沒有「抽象」的概念......除非你使用的庫強制執行方法通過提出錯誤或其他東西來覆蓋。 – zstewart

+1

@zstewart Python確實具有抽象類和方法,如果您嘗試在不覆蓋所有抽象方法的情況下實例化子類,那麼這些抽象類和方法將通過錯誤實施。 –

+0

@SnakesandCoffee *如果您使用'ABCMeta'元類,它屬於zstewart提及的「正在使用的庫」。語言本身沒有抽象類的概念。 – chepner

回答

2

從簡單的方法提高NotImplementedError相當化妝它是一種抽象方法。你仍然可以實例化一個沒有覆蓋其所有繼承的僞抽象方法的類,你只是不能調用的方法。 (或者說,如果您在try聲明中發現NotImplementedError,您甚至可以打電話給他們。)

您可以使用abc.ABCMeta來創建一個真正抽象的類;元類機制可以防止您甚至用未覆蓋的抽象方法實例化類。

import abc 
class MapTile(object): 
    """The base class for all Map Tiles""" 

    __metadata__ = abc.ABCMeta 

    def __init__(self, x, y): 
     """Creates a new tile. 
     Attributes: 
      :param x: The x coordinate of the tile. 
      :param y: The y coordinate of the tile. 
     """ 
     self.x = x 
     self.y = y 

    @abc.abstractmethod 
    def intro_text(self): 
     """Information to be displayed when the player moves into this tile.""" 
     pass 

    # etc. 
+0

謝謝!但是,如果我想保留這個版本的sudo-abstract,只是因爲我還在學習,我能做到這一點,這將是'適當的'? 類WolfRoom(EnemyRoom): ... 高清modify_player(個體經營,the_player): 超(WolfRoom,個體經營).modify_player(the_player) – Zach

+0

@TM在這裏,你確實是需要重寫飾'所有方法abc.abstractmethod'如果你想實例化類。 (當然,如果你只是想要一個派生的抽象類,其子代或後代可以最終覆蓋任何剩餘的抽象方法,你當然可以選擇不要。) – chepner

+0

你能想到爲什麼IDE會給我「......必須實現。 ..「爲modify_player()方法,但不是爲available_actions()方法?是什麼讓這個不同? – Zach

0

是的,你必須在Python中實現所有的抽象方法來實例化它們爲對象(標有@abstractmethod的那些,等等)。然而,你如何實現這些完全取決於你。如果你不打算實例化,你不需要覆蓋所有這些。

例如:

class Animal(object): 

    __metaclass__ = ABCMeta 

    @abstractmethod 
    def eat(thing): 
     pass 

class Slug(Animal): 
    def eat(thing): 
     pass 

這意思是,每個實例化的Animal必須能夠吃,但Slugs什麼都不做,他們吃飯的時候。

+0

謝謝。我最近閱讀,https://www.jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/這是非常有用的。仍然試圖把我的頭圍繞着它。 – Zach

6

我相信這實際上是由於PyCharm檢查員在發現錯誤或至少是關於PEP 8風格的可疑決定時,看看是否有任何未實現的方法會引發NotImplementedError。考慮這個簡單的例子非常相似:

class Base(object): 
    def foo(self): 
     raise NotImplementedError 

    def bar(self): 
     return 0 

class Child(Base): 
    def foo(self): 
     return 0 

class GrandChild(Child): 
    def bar(self): 
     return 1 

my_grand_child = GrandChild() 
print my_grand_child.foo() 

上面的代碼成功地打印出0到輸出,因爲當Python不能找到如GrandChild FOO()執行它查找繼承鏈,並發現它在兒童。然而,由於某些原因,PyCharm檢查預計由此引發NotImplementedError所有類繼承鏈中的各個層面實施。

如果你遵循這種風格在節目與大型繼承結構,你會發現自己是通過實施方法和撥打電話非常冗長的超所有的整個鏈條中,當根本不需要它。就我個人而言,我只是忽略了錯誤,並認爲PyCharm應該更新,以便在它正在檢查的類的任何超類中找到該方法時不顯示它。

+3

請參閱此處的錯誤報告:https://youtrack.jetbrains.com/issue/PY-16776 在5.0.x中標記爲已修復。我有5.0.1,它仍然是一個問題,所以我猜這個解決方案即將推出。 – Coxy

+1

這個問題現在被標記爲5.0.2的固定,我有很多這樣的誤報正在急切地等待下一個版本。 – Zitrax

相關問題