

以下のように異なるインスタンス変数に対して、ほとんど同じ機能を持ったメソッドを定義したいときに、コードをより簡略に書く方法はありますでしょうか？

より具体的には、`add_x()`,`add_y()`,`add_z()`を同一名のメソッド`add()`としたいです。

回答お願いいたします。

[code]

    class test:
        def __init__(self):
            self.x = 1
            self.y = 2
            self.z = 3
    
        def add_x(self):
            self.x += 1
    
        def add_y(self):
            self.y += 1
    
        def add_z(self):
            self.z += 1
    
[/code]


CC BY-SA 4.0
2021-09-14T03:15:34.943
82475
2021-09-14T07:53:59.650
2021-09-14T04:45:38.987
3060
48164
question
python
pythonのクラスで、異なるインスタンス変数に対する似たようなメソッドを簡略化できますか


以下の様に書いてはみましたが、attribute を直接操作する(`t.x += 1` など)のと同じではありますね。

[code]

    from numbers import Number
    
    class test:
        def __init__(self):
            self.x = 1
            self.y = 2
            self.z = 3
    
        def add(self, attr):
            if attr in vars(self):
                v = getattr(self, attr)
                if isinstance(v, Number):
                    setattr(self, attr, v + 1)
    
        def __str__(self):
            return ', '.join(f'{a}: {v}' for a, v in vars(self).items())
    
    if __name__ == '__main__':
        t = test()
        print(t)
    
        t.add('x'), t.add('y'), t.add('z')
        print(t)
    
    # 実行結果
    x: 1, y: 2, z: 3
    x: 2, y: 3, z: 4
    
[/code]


CC BY-SA 4.0
2021-09-14T05:18:00.860
82478
2021-09-14T05:18:00.860
47127
82475
answer


ご質問の具体的な要件からは外れてしまいますが、ほとんど同じ機能の関数をコピペして変数名だけ書き換えるのは[DRY原則](https://xn--
97-273ae6a4irb6e2hsoiozc2g4b8082p.com/%E3%82%A8%E3%83%83%E3%82%BB%E3%82%A4/DRY%E5%8E%9F%E5%89%87/)に反していて美しくない。  
それを避けるために共通の処理を`add`メソッドに集約させたい。  
という隠れた要望があれば、バージョン3.4以降に追加された
[`functools.partialmethod`](https://docs.python.org/ja/3/library/functools.html#functools.partialmethod)
を使ってDRYに記述できます。

下記のサンプルコードではクラス内部でしか呼び出せない`__add`関数を作成し、`add_x`関数などから呼び出すことで処理を集約させています。  
これによってクラス外のコードは変更せずに関数の重複を防ぐことができます。

[code]

    from functools import partialmethod
    
    class test:
        def __init__(self):
            self.x = 1
            self.y = 2
            self.z = 3
    
        # プライベート関数
        def __add(self, varname):
            setattr(self, varname, getattr(self, varname) + 1)
    
        # partialmethodでパブリック関数を定義
        add_x = partialmethod(__add, 'x')
        add_y = partialmethod(__add, 'y')
        add_z = partialmethod(__add, 'z')
    
        def __str__(self):
            return ', '.join(f'{a}: {v}' for a, v in vars(self).items())
    
    t = test()
    print(t)
    t.add_x()
    t.add_z()
    print(t)
    
    # 実行結果
    x: 1, y: 2, z: 3
    x: 2, y: 2, z: 4
    
[/code]


CC BY-SA 4.0
2021-09-14T06:23:03.177
82479
2021-09-14T06:23:03.177
9820
82475
answer


例で提示されたコードはあくまでサンプルなので、実際の問題となっているケースとは解決方法が違うかもしませんが、x, y,
zをそれぞれ変数として定義しているために、メソッドで処理したい対象を動的に変更できないことが問題かと思いました。

変数での定義をやめて、dictで保持するようにすれば動的に対象を変えられると思います。

[code]

    class test:
        def __init__(self):
            self.fields = {"x": 1, "y": 2, "z": 3}
    
        def add(self, name):
            self.fields[name] += 1
    
    t = test()
    t.add("x")
    t.add("y")
    t.add("z")
    print(t.fields)
    
[/code]


CC BY-SA 4.0
2021-09-14T06:59:25.363
82480
2021-09-14T06:59:25.363
28626
82475
answer


> コードをより簡略に書く方法はありますでしょうか？

共通するメソッドを定義したデコレータを書くと、少しはコードが簡略化できると思います。

※フィールドに1加えるコードはmetropolisさんの回答のコードを参考にしました(というか、ほとんど流用です)。

[code]

    #フィールドに１加えるメソッドを追加するクラスのデコレータ
    def add_method(cls):
        from numbers import Number
        def add(cls, attr):
            if attr in vars(cls):
                v = getattr(cls, attr)
                if isinstance(v, Number):
                    setattr(cls, attr, v + 1)
        cls.add = add
        def str(cls):
            return ', '.join(f'{a}: {v}' for a, v in vars(cls).items())
        cls.__str__ = str
        return cls
    
    @add_method
    class test:
        def __init__(self):
            self.x = 1
            self.y = 2
            self.z = 3
    
    if __name__ == '__main__':
        t = test()
        print(t)
        t.add("x")
        t.add("y")
        t.add("z")
        print(t)
    
[/code]


CC BY-SA 4.0
2021-09-14T07:53:59.650
82481
2021-09-14T07:53:59.650
35558
82475
answer