Получение имени экземпляра внутри класса __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"
Ответы
Ответ 1
У экземпляров нет имен. К моменту, когда глобальное имя ThisObject
получает bound в экземпляр, созданный путем вычисления конструктора SomeObject
, конструктор завершил работу.
Если вы хотите, чтобы объект имел имя, просто передайте имя в конструкторе.
def __init__(self, name):
self.name = name
Ответ 2
Ну, есть почти способ сделать это:
#!/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
Итак, если бы вы использовали этот хак, вы должны иметь в виду, что вы должны вызвать SomeObject()
используя простой оператор python:
ThisObject = SomeObject()
Кстати, как еще один пример использования traceback, если вы определяете
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
чтобы напечатать имя переменной и ее значение.
Ответ 3
Вы можете создать метод внутри вашего класса, который проверяет все переменные в текущем фрейме и использует hash()
для поиска переменной self
.
Предлагаемое здесь решение вернет все переменные, указывающие на объект экземпляра.
В приведенном ниже классе isinstance()
используется, чтобы избежать проблем при применении hash()
, так как некоторые объекты, такие как a numpy.array
или list
, например, не сотрясаются.
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']
Ответ 4
Это не сработает, просто представьте это: a = b = TheMagicObjet()
. Имена не влияют на значения, они просто указывают на них.
Ответ 5
В Python все данные хранятся в объектах. Кроме того, имя может быть связано с объектом, после чего это имя можно использовать для поиска этого объекта.
Не имеет никакого значения для объекта, какие имена, если таковые имеются, могут быть связаны. Это может быть связано с десятками разных имен или нет. Кроме того, у Python нет никаких "обратных ссылок", указывающих от объекта к имени.
Рассмотрим следующий пример:
foo = 1
bar = foo
baz = foo
Теперь предположим, что у вас есть целочисленный объект со значением 1, и вы хотите работать назад и найти его имя. Что бы вы напечатали? Три разных имени имеют связанный с ними объект, и все они одинаково действительны.
print(bar is foo) # prints True
print(baz is foo) # prints True
В Python имя - это способ доступа к объекту, поэтому нет возможности напрямую работать с именами. Вы можете искать через различные пространства имен, пока не найдете имя, связанное с объектом интереса, но я не рекомендую это.
Как получить строковое представление переменной в python?
Существует знаменитая презентация под названием "Code Like a Pythonista", которая суммирует эту ситуацию как "Другие языки имеют" переменные "и" Python имеет имена "
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
Ответ 6
Один ужасный, ужасный способ добиться этого - отменить обязанности:
class SomeObject():
def __init__(self, def_name):
self.defined_name = def_name
globals()[def_name] = self
SomeObject("ThisObject")
print ThisObject.defined_name
Если вы хотите поддержать нечто иное, чем глобальное пространство, вам придется сделать что-то еще более ужасное.
Ответ 7
Я думаю, что имена имеют значение, если они являются указателями на любой объект.
не имеет значения, если:
foo = 1
bar = foo
Я знаю, что foo указывает на 1 и указывает на то же значение 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
Ответ 8
Если вы хотите уникальное имя экземпляра для класса, попробуйте __repr__()
или id(self)
class Some:
def __init__(self):
print(self.__repr__()) # = hex(id(self))
print(id(self))
Он напечатает адрес памяти экземпляра, который является уникальным.
Ответ 9
Вдохновленный ответами Унутбу и Саулло Кастро, я создал более сложный класс, который можно даже разделить на подклассы. Это решает то, что просили в вопросе.
"создать значение по умолчанию на основе имени экземпляра класса без передачи дополнительного аргумента. "
Вот что он делает, когда создается экземпляр этого класса или подкласса:
- Поднимайтесь в стеке кадров до первого кадра, который не принадлежит методу текущего экземпляра.
- Осмотрите этот кадр, чтобы получить атрибуты
self.creation_(name/file/module/function/line/text)
.
- Выполните дополнительную проверку, был ли фактически определен объект с именем
self.creation_name
в пространстве имен frame locals(), чтобы на 100% убедиться, что найденное имя creation_name является правильным, или вызвать ошибку в противном случае.
Код:
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
Ответ 10
Лучший способ - это передать имя конструктору, как в выбранном ответе. Однако, если вы ДЕЙСТВИТЕЛЬНО хотите избежать запроса передать имя конструктору, вы можете сделать следующее hack:
Если вы создаете экземпляр с помощью "ThisObject = SomeObject()" из командной строки, вы можете получить имя объекта из командной строки в истории команд:
import readline
import re
class SomeObject():
def __init__(self):
cmd = readline.get_history_item(readline.get_current_history_length())
self.name = re.split('=| ',cmd)[0]
Если вы создаете экземпляр с помощью команды "exec", вы можете справиться с этим:
if cmd[0:4] == 'exec': self.name = re.split('\'|=| ',cmd)[1] # if command performed using 'exec'
else: self.name = re.split('=| ',cmd)[0]