Comments (37)
This is being looked at right now in #70. The current plan is to introduce an install_descriptors
hook that can be overwritten to add obj attributes that are required to run instance_init
.
from traitlets.
@rmorshea I don't see how install_descriptors
solves my problem? I've merged Sylvain's recent pull request into my code, and I can't seem to figure out how to initialize the size on the object. The size is passed in the arguments to the constructor. init_instance
is called from __new__
. If HasTraits
are at the top of the MRO, the child classes won't have a change to install the attribute in their __new__
. Overriding install_descriptors
would work if there were a default size or something, but I need to initialize it to a passed-in value.
from traitlets.
Is size
a static attribute?
from traitlets.
Also it seems odd that the function shape
exists on the TraitType
. Unless that's used to initialize a value on the TraitType
, it should probably be associated with the value of that attribute instead because TraitTypes
are set on HasTraits
classes rather than instances. I may be misunderstanding the application, but it seems that the obj
should be provided to the value rather than the TraitType
, which means this should be something that's handled upon setting signal
.
from traitlets.
@rmorshea : size
is passed to the constructor of the object (the HasTraits
instance). The reason shape
exists on TraitType
is so that the NDArray
class can create an array of the right size. The problem is getting shape
to answer correctly. I don't understand your last sentence probably because I don't understand traitlets well enough.
from traitlets.
Ok, this is a case where you'd want to use dynamic defaults:
old api - _'name'_default
new api (#76) @default
from traitlets.
@rmorshea If this works, it looks like you guys have thought of everything. What connects the x
default to the trait i
?
from traitlets.
In the old api, and using your case, to create a dynamic default generator for the trait signal
you would define a method _signal_default(self)
on the HasTraits
instance which returned the desired value.
This method is not called when creating the instance though. Instead it is called the first time the trait is accessed iff no value has already been set (see TraitType._dynamic_default_callable
).
from traitlets.
@rmorshea Yes, thanks, just figuring it out now. Looking forward to your pull request being accepted. FYI I think your pull request should have x = Int()
—not i = Int()
in the first comment.
from traitlets.
Thanks, glad I could help.
On Thu, Sep 24, 2015 at 8:12 PM Neil [email protected] wrote:
@rmorshea https://github.com/rmorshea Yes, thanks, just figuring it out
now. Looking forward to your pull request being accepted.—
Reply to this email directly or view it on GitHub
#90 (comment).
from traitlets.
@rmorshea I really appreciate it!
from traitlets.
Just reopening this as a reminder to implement install_descriptors
or whatever mechanism will exist to support this.
from traitlets.
@NeilGirdhar, the name has been changed from install_descriptors
to setup_instance
with the introduction of the @defualt
decorator (in addition to HasDescriptors.setup_instance
there is now a MetaHasDescriptors.setup_class
method that is called within the metaclass). The same logic from #70 still applies to setup_instance
though, which means BaseDescriptor.instance_init
only accepts one argument, which is by default, the HasTraits
instance which is running setup_instance
.
from traitlets.
@rmorshea Thanks. Will this allow me to initialize a trait after the instance has done his regular __init__
?
from traitlets.
@NeilGirdhar - see #171
from traitlets.
@rmorshea Thanks a lot! I think that will work for my purpose.
from traitlets.
@NeilGirdhar, this is subjective, but I don't really think there's a need to pass args and kwargs to an instance_init
-like function. Anything that's general enough to be applied to an entire class of descriptors is probably information that's important enough to keep on the HasDescriptors
instance.
from traitlets.
@rmorshea I'm having trouble keeping track of what is what. What's the HasDescriptors
instance? Is that the HasTraits
instance?
from traitlets.
HasTraits
inherits from HasDescriptors
.
from traitlets.
@rmorshea Right, so, instance_init
is called on the trait. Some of my traits are numpy arrays whose shape depends on the HasDescriptors
instance they live on.
from traitlets.
All that is passed to instance_init
is the HasDescriptors
instance the traits live on. So as long as the relevant information (like size
in your example) also lives on that HasDescriptors
instance, you should be fine.
from traitlets.
@rmorshea With your change you mean? Because right now I had to work around it. Also, it looks like you're not passing in the HasDescriptors
instance, but its args
and kwargs
, right? (Although this is good enough for me.)
from traitlets.
Yes, #171 makes this possible. From what I can discern, this cannot be done on master
right now.
from traitlets.
In terms of what's being passed to what, note the difference between HasDescriptors.setup_instance
and BaseDescriptor.instance_init
. While args and kwargs from HasDescriptors
' constructor are passed to setup_instance
, only the HasDescriptors
instance calling setup_instance
(i.e. self
in setup_instance
) is being passed to instance_init
.
from traitlets.
@NeilGirdhar does that make sense?
from traitlets.
@rmorshea Right, but that self
hasn't had its __init__
called yet since we're still in __new__
. So what should I do? Should I inherit from HasTraits
, which is what I'm doing now:
class NDArrayInitializer(HasTraits):
"""
We would like to have this in instance_init, but because of the design of
traitlets, the instance hasn't had its __init__ called until after
instance_init is called. This class allows us to do these in the order we
want.
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
for trait in self.class_traits().values():
if not isinstance(trait, NDArray):
continue
try:
shape = trait.shape(self)
dtype = trait.dtype(self)
trait.set(self, np.zeros(shape, dtype=dtype))
except KeyError:
pass
Or is there a better solution?
from traitlets.
With #171, the new way to do this would be:
class NDArray(TraitType):
def instance_init(self, inst):
shape = inst.shape
dtype = inst.dtype
# do something
print(shape, dtype)
class NDArrayInitializer(HasTraits):
a = NDArray()
def setup_instance(self, *args, **kwargs):
self.shape = kwargs.get('shape', None)
self.dtype = kwargs.get('dtype', None)
super(NDArrayInitializer, self).setup_instance(*args, **kwargs)
ndai = NDArrayInitializer()
from traitlets.
@rmorshea Yup, that is nicer :)
from traitlets.
@rmorshea But wait, why can't I just do this in __new__
? Why even bother with overriding setup_instance
?
from traitlets.
@NeilGirdhar, check out problem case from #171. If we consider the example of NDArray
I just gave, the issue there is that you need to call super().__new__(...)
to get a new instance that you would assign shape
and dtype
to. However super().__new__
would call this chain of functions:
HasDescriptors.__new__ --> HasDescriptors.setup_instance --> NDArray.instance_init
Which will cause NDArray.instance_init
to raise an AttributeError since the new instance won't have the required attributes.
from traitlets.
@rmorshea Right! I forgot about that. I looked at this once and discovered this too. Okay, thanks again for the explanation.
from traitlets.
@NeilGirdhar, in reviewing the thread, we'd talked about using dynamic defaults to solve this problem. Is that approach no longer appropriate? This is what a solution using dynamic defaults would look like:
class NDArray(TraitType): pass
class NDArrayInitializer(HasTraits):
a = NDArray()
@default('a')
def _make_default(self):
return np.zeros(self.shape, dtype=self.dtype)
print(NDArrayInitializer().a)
from traitlets.
@rmorshea Then I have to do that every time I instantiate NDArray
? I'd rather do that in one place.
from traitlets.
What if this were possible (it isn't right now):
class NDArray(TraitType):
def make_dynamic_default(self, inst):
shape = inst.shape
dtype = inst.dtype
# do something
class NDArrayInitializer(HasTraits):
a = NDArray()
print(NDArrayInitializer().a)
from traitlets.
@rmorshea Yeah, that's not bad. I'd rather initialize on object creation rather than trait access personally, so I prefer your earlier solution.
from traitlets.
From your earlier example I noticed you using trait.set
to "create a default value", however using trait.set
to do this isn't quite right since it will cause notifications to be fired. If this is done in instance_init
, notifications that depend on other traits having been initialized will fail.
from traitlets.
@rmorshea I see, thanks. I will use your nicer solution as soon as the relevant changelists are checked in. I am now subscribed to them. :)
from traitlets.
Related Issues (20)
- Consider using mypyc HOT 1
- Promise not to remove Sentinel?
- One test fails HOT 3
- Singletons configurable allow multiple instances HOT 2
- `__doc__` no longer contains the traitlets help string for reference type traitlets (Dict, List, ...) HOT 2
- Fix License metadata in PyPi package
- More typing edge cases
- Uncaught Exception `TypeError: 'ExtendedCompletionFinder' object is not callable` caused by orphaned pyc in site-packages HOT 5
- execution order of trait observers HOT 2
- Traitlets documentation incorrectly shows `__version__`
- Upgrade from traitlets `5.11.2` to `5.12.0` broke script HOT 1
- 5.13.0: pytest is failing with error in tests/test_typing.py HOT 1
- Changed behaviour since v5.12.0 of `traitlets.Set.set` if value is a string HOT 1
- Obeserve not works when showing result in widgets.Output()
- 26 tests fail
- test_complete_custom_completers failing HOT 4
- in get_type_hints(Appliction), TypeError: <class 'traitlets.traitlets.Dict'> is not a generic class HOT 1
- unobserve is not listed in the API in read the docs
- Should there be a way to disable trait notifications? HOT 1
- Collating list traits fails when only one is given - regression in traitlets 5.13.0 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from traitlets.