#python #class #copy
#python #класс #Копировать
Вопрос:
Это довольно сложно объяснить. У меня есть класс, который должен поддерживать метод copy_stateonly()
. Он должен возвращать поврежденную версию объекта, которая содержит только (скопированные) элементы данных, которые я хочу. Я надеюсь, что этот пример объясняет это лучше:
# everything inherits from this
class SuperBase:
def __init__(self):
self.state_var = 3 # this should be copied into future objects
self.non_state_var = 0 # we don't want to copy this
def copy_stateonly(self):
newobj = # ??????????? create instance without calling __init__
newobj.state_var = self.state_var
return newobj
# some clases inherit from this
class Base(SuperBase):
def __init__(self):
SuperBase.__init__(self)
self.isflying = True # we want to copy this, this is state
self.sprite = "sprites/plane_generic.png" # we must drop this
def copy_stateonly(self):
newobj = SuperBase.copy_stateonly(self)
newobj.isflying = self.isflying
return newobj
class A144fighter(Base):
def __init__(self, teamname): # note required __init__ argument
Base.__init__(self)
self.colors = ["black", "grey"] # we want to copy this, this is state
self.name = teamname # we must drop this
def copy_stateonly(self):
newobj = Base.copy_stateonly(self)
newobj.colors = self.colors[:]
return newobj
plane = A144fighter("team_blue")
plane_state = plane.copy_stateonly() # this should return an A144fighter object with only state_var, flying and colors set.
Python 2.7
Ответ №1:
Я не знаю способа создания новых экземпляров классических классов (которые вы использовали в своем примере) без вызова __init__()
. Новые экземпляры классов нового стиля (потомки object
) могут быть созданы с помощью
object.__new__(cls)
где cls
тип объекта, который вы хотели бы создать.
Альтернативой является использование copy.copy()
для копирования, возможно перезаписи __getstate__()
и __setstate__()
для определения того, что должно быть скопировано.
Редактировать: Чтобы создать новый экземпляр классического класса cls
без вызова __init__()
, вы можете использовать следующий хак:
class EmptyClass:
pass
new_instance = EmptyClass()
new_instance.__class__ = cls
new_instance.__dict__.update(whatever)
Комментарии:
1. нет необходимости взламывать:
import types; types.InstanceType(cls)
(илиimport new; new.instance(cls)
)2. второй метод работает только тогда, когда оба класса имеют одинаковую компоновку, что верно для большинства классов, но не будет, когда, например, любой класс использует
__slots__
.3. @TokenMacGuy: Как я уже говорил, второй подход работает только для классических классов.
__slots__
не имеют смысла в этом контексте.4. На самом деле, этот метод работает даже для классов нового стиля (при условии, что
EmptyClass
действительно наследуется отobject
).5. @TokenMacGuy: Для классов нового стиля проще использовать
__new__()
, потому что в противном случае вам пришлось бы охватывать множество особых случаев (__slots__
, встроенные типы среди базовых классов и т.д.)
Ответ №2:
Помните, что у каждого объекта есть атрибут с именем __class__
. Если вы сделаете <object>.__class__
это, вернется класс объекта object (если это имеет смысл). Объект класса можно вызывать, поэтому вы можете добавить круглые скобки в конец, чтобы создать новый экземпляр этого класса.
newobj = self.__class__()
Комментарии:
1. Это все еще вызывает
self.__class__.__init__
, который может иметь требуемые аргументы, как показано в примере, что приводит к ошибке.2. Ах, я думал, вы просто не хотите вызывать его напрямую. Я бы не рекомендовал этого делать, но один из способов обойти это — сделать все аргументы необязательными (поверьте мне) и добавить еще один необязательный аргумент, который должен быть логическим значением. По умолчанию должно быть True. И вы могли бы установить для него значение False, если вы не хотите, чтобы «обязательные» аргументы были обязательными. Если для параметра установлено значение true, проверьте, установлены ли все «обязательные» переменные (т. Е. Они имеют значение None или что-то еще?). Опять же, я бы не рекомендовал это.
Ответ №3:
# everything inherits from this
class SuperBase:
def __init__(self):
self.state_var = 3 # this should be copied into future objects
self.non_state_var = 0 # we don't want to copy this
def __getstate__(self):
return { 'state_var' : self.state_var }
def __str__(self):
return self.__class__.__name__ '(' str(vars(self)) ')'
# some clases inherit from this
class Base(SuperBase):
def __init__(self):
SuperBase.__init__(self)
self.isflying = True # we want to copy this, this is state
self.sprite = "sprites/plane_generic.png" # we must drop this
def __getstate__(self):
state = SuperBase.__getstate__(self)
state['isflying'] = self.isflying
return state
class A144fighter(Base):
def __init__(self, teamname): # note required __init__ argument
Base.__init__(self)
self.colors = ["black", "grey"] # we want to copy this, this is state
self.name = teamname # we must drop this
def __getstate__(self):
state = Base.__getstate__(self)
state['colors'] = self.colors[:]
return state
plane = A144fighter("team_blue")
print plane
import copy
print copy.copy(plane)
# or manually:
import types
print types.InstanceType(plane.__class__, plane.__getstate__())