web-dev-qa-db-ja.com

クラス内のインスタンス名の取得__init __()

Pythonで新しいクラスオブジェクトを作成するときに、追加の引数を渡さずに、クラスのインスタンス名に基づいてデフォルト値を作成できるようにしたいと思います。どうすればこれを達成できますか?これが私が試している基本的な擬似コードです:

class SomeObject():
    defined_name = u""

    def __init__(self, def_name=None):
        if def_name == None:
            def_name = u"%s" % (<INSTANCE NAME>)
        self.defined_name = def_name

ThisObject = SomeObject()
print ThisObject.defined_name   # Should print "ThisObject"
18
Akoi Meexx

インスタンスには名前がありません。グローバル名ThisObjectSomeObjectコンストラクターを評価して作成されたインスタンスにboundを取得するまでに、コンストラクターは実行を終了します。

オブジェクトに名前を付けたい場合は、コンストラクターで名前を渡すだけです。

def __init__(self, name):
    self.name = name
19

まあ、それを行うにはほとんど方法があります:

_#!/usr/bin/env python
import traceback
class SomeObject():
    def __init__(self, def_name=None):
        if def_name == None:
            (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
            def_name = text[:text.find('=')].strip()
        self.defined_name = def_name

ThisObject = SomeObject()
print ThisObject.defined_name 
# ThisObject
_

トレースバックモジュールを使用すると、SomeObject()の呼び出しに使用されるコードを確認できます。少し文字列をラングリングすることで、text[:text.find('=')].strip()def_nameがどうあるべきかを推測できます。

ただし、このハックは脆弱です。たとえば、これはあまりうまく機能しません。

_ThisObject,ThatObject = SomeObject(),SomeObject()
print ThisObject.defined_name
# ThisObject,ThatObject
print ThatObject.defined_name 
# ThisObject,ThatObject
_

したがって、このハックを使用する場合は、単純なpythonステートメントを使用してSomeObject()を呼び出す必要があることに注意する必要があります。

_ThisObject = SomeObject()
_

ちなみに、トレースバックを使用するさらなる例として、

_def pv(var):
    # stack is a list of 4-tuples: (filename, line number, function name, text)
    # see http://docs.python.org/library/traceback.html#module-traceback
    #
    (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
    # ('x_traceback.py', 18, 'f', 'print_var(y)')
    print('%s: %s'%(text[text.find('(')+1:-1],var))
_

その後、あなたは呼び出すことができます

_x=3.14
pv(x)
# x: 3.14
_

変数名とその値の両方を出力します。

22
unutbu

現在のフレーム内のすべての変数をチェックするメソッドをクラス内に作成し、hash()を使用してself変数を探すことができます。

ここで提案するソリューションは、インスタンスオブジェクトを指すすべての変数を返します。

以下のクラスでは、isinstance()を適用する際の問題を回避するために、hash()が使用されています。これは、たとえば、numpy.arraylistなどの一部のオブジェクトがハッシュ化できません。

import inspect
class A(object):
    def get_my_name(self):
        ans = []
        frame = inspect.currentframe().f_back
        tmp = dict(frame.f_globals.items() + frame.f_locals.items())
        for k, var in tmp.items():
            if isinstance(var, self.__class__):
                if hash(self) == hash(var):
                    ans.append(k)
        return ans

次のテストが実行されました。

def test():
    a = A()
    b = a
    c = b
    print c.get_my_name()

結果は次のとおりです。

test()
#['a', 'c', 'b']
6

これは機能しません。想像してみてください:a = b = TheMagicObjet()。名前は値に影響を与えません。名前はそれらを指すだけです。

3
Jochen Ritzel

これを達成するための1つの恐ろしい、恐ろしい方法は、責任を逆転させることです。

class SomeObject():
    def __init__(self, def_name):
        self.defined_name = def_name
        globals()[def_name] = self

SomeObject("ThisObject")
print ThisObject.defined_name

グローバルスコープ以外の何かをサポートしたい場合は、さらにひどいことをしなければならないでしょう。

2

Pythonでは、すべてのデータがオブジェクトに保存されます。さらに、名前をオブジェクトにバインドした後、その名前を使用してそのオブジェクトを検索できます。

バインドされている可能性のある名前があれば、オブジェクトに違いはありません。何十もの異なる名前にバインドされている場合もあれば、バインドされていない場合もあります。また、Pythonには、オブジェクトから名前を指す「バックリンク」はありません。

この例を考えてみましょう。

foo = 1
bar = foo
baz = foo

ここで、値が1の整数オブジェクトがあり、逆方向に作業してその名前を見つけたいとします。何を印刷しますか? 3つの異なる名前には、そのオブジェクトがバインドされており、すべて同じように有効です。

print(bar is foo) # prints True
print(baz is foo) # prints True

Pythonでは、名前はオブジェクトにアクセスする方法であるため、名前を直接操作する方法はありません。目的のオブジェクトにバインドされている名前が見つかるまで、さまざまな名前空間を検索できますが、これはお勧めしません。

Pythonで変数の文字列表現を取得するにはどうすればよいですか?

この状況を「他の言語には「変数」があり」「Pythonには「名前」がある」と要約した「Pythonistaのようなコード」という有名なプレゼンテーションがあります。

http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables

2
steveha

クラスに一意のインスタンス名が必要な場合は、__repr__()またはid(self)を試してください。

class Some:
    def __init__(self):
        print(self.__repr__())  # = hex(id(self))
        print(id(self))

一意のインスタンスのメモリアドレスを出力します。

0

名前がオブジェクトへのポインタである場合は重要だと思います。次の場合は関係ありません。

foo = 1
bar = foo

Fooは1を指し、barは同じメモリ空間内の同じ値1を指していることを私は知っています。しかし、オブジェクトを追加する関数を使用してクラスを作成するとします。

Class Bag(object):
   def __init__(self):
       some code here...
   def addItem(self,item):
       self.__dict__[somewaytogetItemName] = item

したがって、以下のようにクラスバッグをインスタンス化すると、次のようになります。

newObj1 = Bag()
newObj2 = Bag()
newObj1.addItem(newObj2)I can do this to get an attribute of newObj1:
newObj1.newObj2
0
andrexterz

UnutbuとSaulloCastroの回答に触発されて、サブクラス化することもできる、より洗練されたクラスを作成しました。それは質問で求められたものを解決します。

「余分な引数を渡さずに、クラスのインスタンス名に基づいてデフォルト値を作成します。」

このクラスまたはサブクラスのインスタンスが作成されたときの動作は次のとおりです。

  1. 現在のインスタンスのメソッドに属していない最初のフレームまで、フレームスタックを上に移動します。
  2. このフレームを調べて、属性self.creation_(name/file/module/function/line/text)を取得します。
  3. self.creation_nameという名前のオブジェクトが実際にフレームのlocals()名前空間で定義されているかどうかを追加チェックして、見つかったcreation_nameが正しいことを100%確認するか、そうでない場合はエラーを発生させます。

コード:

import traceback, threading, time

class InstanceCreationError(Exception):
    pass

class RememberInstanceCreationInfo:
    def __init__(self):
        for frame, line in traceback.walk_stack(None):
            varnames = frame.f_code.co_varnames
            if varnames is ():
                break
            if frame.f_locals[varnames[0]] not in (self, self.__class__):
                break
                # if the frame is inside a method of this instance,
                # the first argument usually contains either the instance or
                #  its class
                # we want to find the first frame, where this is not the case
        else:
            raise InstanceCreationError("No suitable outer frame found.")
        self._outer_frame = frame
        self.creation_module = frame.f_globals["__name__"]
        self.creation_file, self.creation_line, self.creation_function, \
            self.creation_text = \
            traceback.extract_stack(frame, 1)[0]
        self.creation_name = self.creation_text.split("=")[0].strip()
        super().__init__()
        threading.Thread(target=self._check_existence_after_creation).start()

    def _check_existence_after_creation(self):
        while self._outer_frame.f_lineno == self.creation_line:
            time.sleep(0.01)
        # this is executed as soon as the line number changes
        # now we can be sure the instance was actually created
        error = InstanceCreationError(
                "\nCreation name not found in creation frame.\ncreation_file: "
                "%s \ncreation_line: %s \ncreation_text: %s\ncreation_name ("
                "might be wrong): %s" % (
                    self.creation_file, self.creation_line, self.creation_text,
                    self.creation_name))
        nameparts = self.creation_name.split(".")
        try:
            var = self._outer_frame.f_locals[nameparts[0]]
        except KeyError:
            raise error
        finally:
            del self._outer_frame
        # make sure we have no permament inter frame reference
        # which could hinder garbage collection
        try:
            for name in nameparts[1:]: var = getattr(var, name)
        except AttributeError:
            raise error
        if var is not self: raise error

    def __repr__(self):
        return super().__repr__()[
               :-1] + " with creation_name '%s'>" % self.creation_name

簡単な例:

class MySubclass(RememberInstanceCreationInfo):
    def __init__(self):
        super().__init__()

    def print_creation_info(self):
        print(self.creation_name, self.creation_module, self.creation_function,
                self.creation_line, self.creation_text, sep=", ")

instance = MySubclass()
instance.print_creation_info()
#out: instance, __main__, <module>, 68, instance = MySubclass()

作成名を正しく判別できない場合、エラーが発生します。

variable, another_instance = 2, MySubclass()

# InstanceCreationError: 
# Creation name not found in creation frame.
# creation_file: /.../myfile.py 
# creation_line: 71 
# creation_text: variable, another_instance = 2, MySubclass()
# creation_name (might be wrong): variable, another_instance
0
TheoRet