質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.98%

  • Python 3.x

    4136questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python クラス内でクラスメソッドを使用したい

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 255

_Victorique__

score 883

題意の通り、クラス内でクラスメソッドを使用したいのですができません。下記に例を示しています。
これを実現するにはどうすれば良いでしょうか?

class Test:

    _num = cls.__set_num()

    @classmethod
    def __set_num(cls):
        return 1

#clsは定義されていませんとなる
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • miyahan

    2017/10/12 22:40

    自分の知る限り方法が思いつかないのですが、なにかクラス変数の定義時にクラスメソッドを呼ぶ具体的なシチュエーションを教えていただけると問題解決に協力できるかもしれません

    キャンセル

  • _Victorique__

    2017/10/12 22:49

    ユーティリティクラスとして実装しているのですが、クラス変数として持たせたい情報をファイルから読み込まなきゃいけなく、このような事態になっています。何か別のアプローチにした方が良いでしょうか?

    キャンセル

回答 3

checkベストアンサー

+1

> クラス変数として持たせたい情報をファイルから読み込まなきゃいけなく

なるほど。共通のやや重い前処理があるという感じですね。

まずクラス変数の定義で自身のメソッドを呼ぶのは言語仕様上おそらく無理だと思います。代替案を考えてみたのですが、スマートに実装する方法がなかなか思いつきませんでした。


案1:まず クラスに閉じたい && クラスメソッドとして呼びたいんだ!というモチベーションに全振りした実装。

class TestStatic(object):
    @classmethod
    def __get_num(cls):
        if not hasattr(cls, '__num'):
            print('1回だけやる処理')
            cls.__num = 123456
        return cls.__num

    @classmethod
    def say_hello(cls):
        num = cls.__get_num()
        print('こんにちは、{}回お会いしましたね。'.format(num))

"""
>>> TestStatic.say_hello()
1回だけやる処理
こんにちは、123456回お会いしましたね。
>>> TestStatic.say_hello()
1回だけやる処理
こんにちは、123456回お会いしましたね。
"""

クラス変数は無理っぽいのでクラスメソッドで代替。


案2:次に クラスに閉じたい && インスタンス化してもいいや なパターン。

from werkzeug.utils import cached_property

class TestInstance(object):
    @cached_property
    def __num(self):
        print('1回だけやる処理')
        return 123456

    def say_hello(self):
        print('こんにちは、{}回お会いしましたね。'.format(self.__num))


"""
>>> test = TestInstance()
>>> test.say_hello()
1回だけやる処理
こんにちは、123456回お会いしましたね。
>>> test.say_hello()
こんにちは、123456回お会いしましたね。
"""

プロパティが使えるのでコードはすっきり、でも呼び出し側でインスタンスを管理しなければならずイケてない。

なお前処理は werkzeug.utils.cached_property を使って結果をキャッシュし2回目以降はそれを返すようにしています。

ちなみに案1と案2のいいとこどりをした「クラスプロパティ」なるものを作った方もいらっしゃいます:python メタクラスを使ってクラスプロパティを実装する - fakatatuku’s blog


案3:最後にクラス使わない式。

from functools import lru_cache

@lru_cache(maxsize=1)
def __get_num():
    print('1回だけやる処理')
    return 123456

def say_hello():
    print('こんにちは、{}回お会いしましたね。'.format(__get_num()))

"""
>>> say_hello()
1回だけやる処理
こんにちは、123456回お会いしましたね。
>>> say_hello()
こんにちは、123456回お会いしましたね。
"""

こちらは、functools.lru_cach を使って前処理をキャッシュしています。

私も(PHPから入門したためか)グローバルより狭くローカル変数より広い → クラス変数という考えがありましたが、Python にはスクリプトファイルが「モジュール」という名前空間のような概念になっているので、わざわざ class に詰め込む必要もないのかなーと最近感じています。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/13 00:23

    私も案3でいいかなーって思い始めました!
    でもなんか悔しいですよね( ;∀;)
    たくさん考えていただきありがとうございました!参考になりました!

    キャンセル

0

追記

間違いでした。削除したいところですがサイトの削除ポリシーで許されていないのでこのままで失礼します。


単に定義する前に呼び出しているのが問題です(defはであって、実行された時に名前空間に関数の名前を束縛するのです)。

class Test:
    @classmethod
    def __set_num(cls):
        return 1

    print(Test.__set_num())

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/13 00:16

    正直モジュールメソッドにしない理由がわからなかったりしますが。

    キャンセル

  • 2017/10/13 00:24

    回答ありがとうございます!結果モジュールメソッドにすることになりました!

    キャンセル

-1

cls__set_numの引数であって、その外では定義されていないので当然使えません。

質問者さんがやりたいことがいまひとつ分からないですが、こうすればエラーはでないです。

class Test:
    @classmethod
    def _set_num(cls):
        return 1

Test._num = Test._set_num()

クラスの定義内でクラスメソッドにアクセスすることはできません。
定義したあとにメンバーを編集することはできます。

def __set_num(cls):とアンダースコアを2つ先頭につけると、name manglingにより外からアクセスしにくくなるように自動的に名前が変えられます。

なので外からクラスメソッドをアクセスできるように上のコードではアンダースコアを1つにしました。

参考
https://stackoverflow.com/questions/13900515/how-can-i-access-a-classmethod-from-inside-a-class-in-python

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    4136questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。