Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow default_value to Instance #518

Closed
krey opened this issue Apr 2, 2019 · 3 comments
Closed

Allow default_value to Instance #518

krey opened this issue Apr 2, 2019 · 3 comments

Comments

@krey
Copy link

krey commented Apr 2, 2019

I would like to have a trait that is a pandas dataframe:

from traitlets import HasTraits, Instance
import pandas as pd

class Test(HasTraits):
    foo = Instance(pd.DataFrame, kw={'columns': ['int_col', 'float_col'], 'dtype': float})

test = Test()

Unfortunately, the constructor pd.DataFrame does not have a dtypes argument where I could specify the datatypes by column. (This issue is old enough to go to school now pandas-dev/pandas#4464)

>> print(test.foo.dtypes)
int_col      float64
float_col    float64
dtype: object

And the only way to initialize it properly is:

>> test.foo = pd.DataFrame(columns=['int_col', 'float_col']).astype({'int_col': int, 'float_col': float})
>> print(test.foo.dtypes)
int_col        int64
float_col    float64
dtype: object
@rmorshea
Copy link
Contributor

rmorshea commented Apr 2, 2019

@krey
Copy link
Author

krey commented Apr 2, 2019

Thanks @rmorshea

It's a workaround for sure, but morally it should be part of the trait, not the class, no?

@rmorshea
Copy link
Contributor

rmorshea commented Apr 2, 2019

@krey unfortunately no. Default values that are declared without @default are basically like class attributes. The problem with class attributes is that they are shared among all their instances. We can demonstrate an analogous problem with the following code:

class A:
    my_list= []

a_1 = A()
a_1.my_list.append(1)

a_2 = A()
assert a_2.my_list== [1]

We might expect to assert a_2.my_list == [] but because my_list is a class attribute both a_1 and a_2 share the same list:

a_1.my_list is a_2.my_list

If it were possible to declare:

class A(HasTraits):
    my_list= Instance(list, default=[])

You'd encounter a similar problem.

Forcing you to use @default may not be semantically pretty, but it is functionally correct because you return a new value for each instance you create:

class A(HasTraits):
    my_list = Instance(list)
 
    @default("my_list")
    def _my_list_default(self):
        return []

If you don't specify @default then Instance actually generates a dynamic default for you from the arguments you provide in its constructor:

https://github.com/ipython/traitlets/blob/master/traitlets/traitlets.py#L1841

@rmorshea rmorshea closed this as completed Apr 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants