I have a case where, evidently, __init__()
is called as a result of a method call. (Not precisely sure why; suspect it may have to do with needing to return a copy.)
Example of the class's design:
import copy
import pandas as pd
class TSeries(pd.Series):
# See:
# https://pandas.pydata.org/pandas-docs/stable/internals.html
_metadata = ['some_new_attr']
def __init__(self, *args, **kwargs):
# Copy needed because we would otherwise inadvertantly alter
# mutable arguments such as pd.Series, TSeries, etc.
args = tuple(copy.deepcopy(arg) for arg in args)
some_new_attr = kwargs.pop('some_new_attr', None)
super().__init__(*args, **kwargs)
print('WE ARE "IN IT."')
# Put a calculation here that that we *don't* want to call
# if class instance is instantiated as a result of
# of self.new_method().
#
# _do_some_conditional_calculation(self)
@property
def _constructor(self):
# Used when a manipulation result has the same
# dimesions as the original. Fairly sure
# self.new_method() uses this.
return TSeries
def new_method(self):
return self * 100
Calling a method, as a result of which __init__()
is called:
>>> x = TSeries([1, 2, 3])
WE ARE "IN IT."
>>> x.new_method()
WE ARE "IN IT." # <-- __init__() called
0 100
1 200
2 300
dtype: int64
My desire is to do some inplace operation on self
within __init__()
, but not if the call occurred because of a method call. (Or, more directly, "do the operation only if the user instantiates an instance of TSeries
directly.")
How can I differentiate between these two cases?
Update - this illustrates what I'm trying to do, but seems dangerous.
IS_METHOD_CALL = False
class TSeries(pd.Series):
# See:
# https://pandas.pydata.org/pandas-docs/stable/internals.html
_metadata = ['some_new_attr']
def __init__(self, *args, **kwargs):
# Copy needed because we would otherwise inadvertantly alter
# mutable arguments such as pd.Series, TSeries, etc.
args = tuple(copy.deepcopy(arg) for arg in args)
some_new_attr = kwargs.pop('some_new_attr', None)
super().__init__(*args, **kwargs)
print('WE ARE "IN IT."')
# Put a calculation here that that we *don't* want to call
# if class instance is instantiated as a result of
# of self.new_method().
global IS_METHOD_CALL
if not IS_METHOD_CALL:
print('Some conditional calculation')
IS_METHOD_CALL = False
@property
def _constructor(self):
# Used when a manipulation result has the same
# dimesions as the original. Fairly sure
# self.new_method() uses this.
return TSeries
def new_method(self):
# We'd need to do within every single method...
global IS_METHOD_CALL
IS_METHOD_CALL = True
return self * 100
Conditional calc is skipped:
>>> x = TSeries([1, 2, 3])
WE ARE "IN IT."
Some conditional calculation
>>> x.new_method()
WE ARE "IN IT."
0 100
1 200
2 300
dtype: int64
答案 0 :(得分:1)
I think you'll have to explicitly tell TSeries.__init__
to perform the conditional calculation. There's nothing the really distinguishes between explicit and implicit calls to TSeries.__new__
(via type.__call__(TSeries, ...)
).
class TSeries(pd.Series):
def __init__(self, ..., extra_config=False):
...
if extra_config:
self._do_some_conditional_calculation()
Now, the only way to execute _do_some_conditional_calculation
is to request it explicitly: x = TSeries([1,2,3], extra_config=True)
.