2016-08-13 6 views
-2

私は 'Movable Piece'というクラスを持っています。もちろん、私はこのクラスのすべてのインスタンスを動かしたいと思います。そのために、私はMovementという別のクラスがいいと思っていました。移動するために他のものが必要な場合に備えて再利用可能です。また、私はコードでmy_piece.move.upがどのように見えるのかが大好きです。Pythonでインスタンスのメソッドを動的に作成

問題は、ピースを動かす関数がユーザー定義である可能性があるため、ピースによってインスタンス化されたMovementsクラスのインスタンスのメソッドを動的に設定する必要があることを認識したときに発生します。どうすればこれを達成できますか?コードが私がしたいことを明確にすると思います。

class MovablePiece(Piece): 
    class Movements: 
     def __init__(self, piece, movement_functions=None): 
      if movement_functions is None: 
       self.__default_movements(piece) 
      else: 
       self.__set_movements(movement_functions) 

     def __default_movements(self, piece): 
      def up(): return piece.move(piece.surroundings[Direction.UP]) 
      def right(): return piece.move(piece.surroundings[Direction.RIGHT]) 
      def down(): return piece.move(piece.surroundings[Direction.DOWN]) 
      def left(): return piece.move(piece.surroundings[Direction.LEFT]) 
      self.__set_movements([up, right, down, left]) 

     def __set_movements(self, movement_functions): 
      for movement_function in movement_functions: 
       setattr(self, movement_function.__name__, movement_function) 

    def __init__(self, letter, name, movements=None, walkable=False): 
     Piece.__init__(self, letter, name, walkable) 
     self.move = MovablePiece.Movements() 

これは、もちろん、動作しません:SETATTRは、私は非常に理にかなっていますが、それの要点を得ないと思う属性、としての機能を設定しようとしています。

私はmy_piece.move.rightをしようとするとこれはエラーです:

Traceback (most recent call last): 
    File "main.py", line 45, in <module> 
    screen.show() 
    File "/home/joaquin/Documents/escape/ludema/screen.py", line 12, in show 
    function() 
    File "main.py", line 35, in control_bruma 
    mappings[action]() 
    File "/home/joaquin/Documents/escape/ludema/pieces.py", line 78, in right 
    def right(): return piece.move(piece.surroundings[Direction.RIGHT]) 
TypeError: 'Movements' object is not callable 

同様の問題が、私は(彼らは実際には「自己」を必要としないよう)staticmethodsする方法を強制する場合:

Traceback (most recent call last): 
    File "main.py", line 45, in <module> 
    screen.show() 
    File "/home/joaquin/Documents/escape/ludema/screen.py", line 12, in show 
    function() 
    File "main.py", line 35, in control_bruma 
    mappings[action]() 
TypeError: 'staticmethod' object is not callable 
+0

'setattr()'は正常に動作するはずです。問題は、関数が 'self'最初の引数を受け入れるように定義されていないため、適切なメソッドではない可能性があります。 – martineau

+0

それはありません。私はトレースバックによって与えられたエラーを添付します。 – joaquinlpereyra

+0

@martineauパラメータを取らない静的メソッドを作成しようとしたときのトレースバックを追加しました:) – joaquinlpereyra

答えて

1

IMHOあなたは、この回答がいくつかの追加のヒントを追加する可能性があるという質問にmvceを提供する必要があります。いずれの場合も、コードの欠けている部分を推測する作業例があります。

class Piece(object): 

    def __init__(self, letter, name, walkable): 
     self.letter = letter 
     self.name = name 
     self.walkable = walkable 


class Movements: 

    def __init__(self, piece, movement_functions=None): 
     if movement_functions is None: 
      self.__default_movements(piece) 
     else: 
      self.__set_movements(movement_functions) 

    def __default_movements(self, piece): 
     def up(): print("up") 

     def right(): print("right") 

     def down(): print("down") 

     def left(): print("left") 
     self.__set_movements([up, right, down, left]) 

    def __set_movements(self, movement_functions): 
     for movement_function in movement_functions: 
      setattr(self, movement_function.__name__, movement_function) 


class MovablePiece(Piece): 

    def __init__(self, letter, name, movements=None, walkable=False): 
     Piece.__init__(self, letter, name, walkable) 
     self.move = Movements(self) 

p = MovablePiece("foo", "foo") 
for direction in ["up", "right", "down", "left"]: 
    getattr(p.move, direction)() 

は、もう一つの選択肢は、このような何かをコーディングすることになります。

もちろん
class UpMovement(object): 

    def __init__(self, piece): 
     self.piece = piece 
     self.name = "up" 

    def move(self): 
     if self.piece.walkable: 
      print("up") 
     else: 
      print("piece not walkable to go up") 


class DownMovement(object): 

    def __init__(self, piece): 
     self.piece = piece 
     self.name = "down" 

    def move(self): 
     if self.piece.walkable: 
      print("down") 
     else: 
      print("piece not walkable to go down") 


class LeftMovement(object): 

    def __init__(self, piece): 
     self.piece = piece 
     self.name = "left" 

    def move(self): 
     if self.piece.walkable: 
      print("left") 
     else: 
      print("piece not walkable to go left") 


class RightMovement(object): 

    def __init__(self, piece): 
     self.piece = piece 
     self.name = "right" 

    def move(self): 
     if self.piece.walkable: 
      print("right") 
     else: 
      print("piece not walkable to go right") 


class Piece(object): 

    def __init__(self, letter, name, walkable): 
     self.letter = letter 
     self.name = name 
     self.walkable = walkable 


class Movements(object): 

    def __init__(self): 
     pass 


class MovablePiece(Piece): 

    def __init__(self, letter, name): 
     Piece.__init__(self, letter, name, True) 
     movements = [ 
      UpMovement(self), 
      DownMovement(self), 
      LeftMovement(self), 
      RightMovement(self) 
     ] 

     self.move = Movements() 
     for m in movements: 
      setattr(self.move, m.name, m.move) 


class StaticPiece(Piece): 

    def __init__(self, letter, name): 
     Piece.__init__(self, letter, name, False) 
     movements = [ 
      UpMovement(self), 
      DownMovement(self), 
      LeftMovement(self), 
      RightMovement(self) 
     ] 

     self.move = Movements() 
     for m in movements: 
      setattr(self.move, m.name, m.move) 

p1 = MovablePiece("foo1", "foo1") 

for name in ["up", "down", "left", "right"]: 
    getattr(p1.move, name)() 

p2 = StaticPiece("foo2", "foo2") 

for name in ["up", "down", "left", "right"]: 
    getattr(p2.move, name)() 

、あなたがここにあるクラスを抽象化することをoverengineerことができ、クラスの設計がはるかに優れた作りとSOLID設計原則を適用します。いずれにしても、基本的に問題はダイナミックなものをピースにアタッチする方法だったので、可能な解決策を次に示します:

+0

ありがとうございました!残念ながら、最初のオプションは、エンドプログラマから多くの行を必要とし、混乱を招きやすい(私はライブラリをプログラミングしているので、エンドユーザもプログラマです):私は本当にPieceの作成を動きの創造。 2番目のオプションは、多くの不要なクラスを作成します。私は最終的にそれを解決した、あなたが私がそれをやった方法を見て興味があれば私は自分の質問に答えます。 – joaquinlpereyra

+0

@joaquinlpereyraあなたの答えを見つけられたらうれしいです。私は質問がそれ以上の要件を指定していなかったので、私は多くを推測しているのが分かるように、多くのソリューションを提供していませんでした。次回は[mcve](http://stackoverflow.com/help/mcve)にもっと多くの制約を加えて、より良い回答を得ることができます。がんばろう。 – BPL

0

これは私が最終的にやったところです。このサンプルは再現できませんが、ミックスには多すぎるクラスがあり、この正確な問題の可読性と理解力を改ざんすると思います。それにもかかわらず、コードはgithubです。

特に、パラメータを使用しなくても機能を静的にする必要はありませんでした。明らかに、Pythonはそれをあなたのために何とかします。

class MovablePiece(Piece): 

    class Movements: 
     """A simple interface to represent the movements of the MovablePiece. 
     """ 
     def __init__(self, piece, movement_functions=None): 
      if movement_functions is None: 
       self.__default_movements(piece) 
      else: 
       self.__set_movements(movement_functions) 

     def __default_movements(self, piece): 
      def up(): return piece.move_to_tile(piece.surroundings[Direction.UP]) 
      def right(): return piece.move_to_tile(piece.surroundings[Direction.RIGHT]) 
      def down(): return piece.move_to_tile(piece.surroundings[Direction.DOWN]) 
      def left(): return piece.move_to_tile(piece.surroundings[Direction.LEFT]) 
      self.__set_movements([up, right, down, left]) 

     def __set_movements(self, movement_functions): 
      for movement_function in movement_functions: 
       setattr(self, movement_function.__name__, movement_function) 

    def __init__(self, letter, name, movements=None, walkable=False): 
     Piece.__init__(self, letter, name, walkable) 
     self.move = MovablePiece.Movements(self) 

    def _unsafe_move_to_tile(self, tile): 
     """Move the object in a certain direction, if it can: 
     That means: unlink the piece from its current tile and link it 
     to the new tile; unless there's a piece in the destiny tile already. 

     Return True if could move there, False is possition was already 
     ocuppied. 

     Can raise a PieceIsNotOnATileError if the piece hasn't been put on a 
     map prior to moving or a PieceIsNotOnThisBoardError if the piece 
     you're trying to move has an associated tile in another board, not 
     the one where the destinity tile is. 
     """ 
     if not self.home_tile: 
      raise PieceIsNotOnATileError 
     if self.home_tile.board is not tile.board: 
      raise PieceIsNotOnThisBoardError 

     if tile.piece is not None: 
      tile.piece.on_touch_do(touching_piece=self) 
      if not tile.piece.walkable: 
       return False 

     self.home_tile.piece = None 
     tile.piece = self 
     return True 

    def move_to_tile(self, tile): 
     if tile: 
      try: 
       return self._unsafe_move_to_tile(tile) 
      except (PieceIsNotOnATileError, PieceIsNotOnThisBoardError): 
       return False 
     else: 
      return False 
+0

FWIW:静的メソッドをクラスインスタンスに追加する必要はありませんでした。クラスインスタンスではなく、 'self'です。詳細については、[[既存のオブジェクトへのメソッドの追加] _(http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object-instance)]の回答を参照してください。 – martineau

関連する問題