{"action":"opened","issue":{"url":"https://api.github.com/repos/ioam/param/issues/113","labels_url":"https://api.github.com/repos/ioam/param/issues/113/labels{/name}","comments_url":"https://api.github.com/repos/ioam/param/issues/113/comments","events_url":"https://api.github.com/repos/ioam/param/issues/113/events","html_url":"https://github.com/ioam/param/issues/113","id":66478122,"number":113,"title":"Proposal for parameter attribute inheritance changes in param v2","user":{"login":"ceball","id":1929,"avatar_url":"https://avatars.githubusercontent.com/u/1929?v=3","gravatar_id":"","url":"https://api.github.com/users/ceball","html_url":"https://github.com/ceball","followers_url":"https://api.github.com/users/ceball/followers","following_url":"https://api.github.com/users/ceball/following{/other_user}","gists_url":"https://api.github.com/users/ceball/gists{/gist_id}","starred_url":"https://api.github.com/users/ceball/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceball/subscriptions","organizations_url":"https://api.github.com/users/ceball/orgs","repos_url":"https://api.github.com/users/ceball/repos","events_url":"https://api.github.com/users/ceball/events{/privacy}","received_events_url":"https://api.github.com/users/ceball/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-04-05T20:12:53Z","updated_at":"2015-04-05T20:12:53Z","closed_at":null,"body":"(draft proposal!)\r\n\r\nCurrently, Parameter attribute inheritance is a bit inconsistent (or\r\nat least not well documented). By 'Parameter attribute', we mean\r\ne.g. the ``default`` attribute (slot) of Parameter, or the ``bounds``\r\nslot of Number. The main problem in param v1 is that ``None`` was\r\noriginally used to mean 'not set', but eventually we started to use\r\n``None`` as a valid value too.\r\n\r\nWe're proposing to address these inheritance-related inconsistencies\r\nin param v2. Below we list some proposed changes, and highlight\r\ncurrent inconsistencies using some example classes.\r\n\r\nChanges:\r\n\r\n* All parameter attributes will default to `NotImplemented` (\"not\r\n  set\"). Currently, some attributes default to None, while many\r\n  default to other things (e.g. 1.0 for Number).\r\n\r\n* If not set, a Parameter attribute will inherit from the attribute of\r\n  a Parameter of the same name defined in the superclass of the owning\r\n  Parameterized. Currently, this inheritance may or may not happen\r\n  (see examples later).\r\n\r\n* If a Parameter attribute fails to get a value when inheriting as\r\n  above, there will be an exception (nothing can be left 'unset').\r\n\r\n* Parameters will no longer have a default value at the Parameter\r\n  class level (it's up to the person using that Parameter to supply\r\n  such a default). Note that this means e.g. the List parameter can no\r\n  longer default to an empty list (at the List class level).\r\n\r\n* Parameter class authors can, however, declare default values for\r\n  attributes other than `default` (i.e. for the meta parameters). For\r\n  instance, the author of the List parameter class can say\r\n  `instantiate=True` by default for List parameters; then, when a List\r\n  parameter is declared in a Parameterized class, if the class author\r\n  does not specify a value for `instantiate`, and the Parameterized\r\n  class's superclass (if any) does not have a List (subclass) of the\r\n  same name with a value for `instantiate` specified, then\r\n  `instantiate` will be True for that List parameter.\r\n  \r\n* If you define a parameter x in a subclass, that parameter will have\r\n  to be the same type as/a subclass of the x parameter class in owning\r\n  parameterized's superclass. Currently we don't enforce this.\r\n\r\n\r\n\r\nSome classes (slightly modified from current reality for brevity) for\r\nexamples...\r\n\r\n\r\n```python\r\n    class Parameter(object):\r\n        ...\r\n        def __init__(self,\r\n                     default=None,\r\n                     doc=None,\r\n                     allow_None=False)\r\n\r\n\r\n    class Number(Parameter):\r\n        ...\r\n        def __init__(self,\r\n                     default=0.0,\r\n                     bounds=None,\r\n                     inclusive_bounds=(True,True),\r\n                     **kw)\r\n\r\n    class List(Parameter):\r\n        ...\r\n        def __init__(self,\r\n                     default=[],\r\n                     bounds=(0,None),\r\n                     **kw)\r\n                                         \r\n\r\n    class A(Parameterized):\r\n        a = Number(default=None, \r\n                   doc=\"a number\",\r\n                   allow_None=True,\r\n                   bounds=(0.5,1.5), \r\n                   inclusive_bounds=(True,False))\r\n\r\n        b = List(default=[1,2,3],\r\n                 bounds=(3,3))\r\n\r\n\r\n    class B(A):\r\n        a = Number(default=1.2)\r\n        b = List(default=None)\r\n\r\n\r\n    class C(B):\r\n        a = Number(default=None)\r\n        b = Number(default=10)\r\n```\r\n\r\n\r\nFor the classes above, considering first the 'a' parameter, I think\r\nthe following is currently how things end up after the parameterized\r\nclasses have been created:\r\n\r\n```python\r\n  A.a\r\n    default=0.0 \r\n    doc=\"a number\"\r\n    allow_None=True\r\n    bounds=(0.5,1.5)\r\n    inclusive_bounds=(True,False)\r\n        \r\n  B.a\r\n    default=1.2\r\n    doc=\"a number\"\r\n    allow_None=False\r\n    bounds=(0.5,1.5)\r\n    inclusive_bounds=(True,True)\r\n        \r\n  C.a\r\n    default=0.0\r\n    doc=\"a number\"\r\n    allow_None=False\r\n    bounds=(0.5,1.5)\r\n    inclusive_bounds=(True,True)\r\n```    \r\n\r\nSome notes:\r\n\r\n  * A.a default should be None\r\n\r\n  * B.a allow_None should be True? Maybe: this will make the logic\r\n    clearer with fewer special cases. But maybe not: it's ok for a\r\n    subclass to have more restriction than its superclass, and None\r\n    might not be meaningful for B even though it's meaningful for A.\r\n\r\n  * B.a inclusive_bounds should be (True, False).\r\n\r\n  * C.a default should be None.\r\n\r\n\r\n\r\nNow considering the 'b' parameter...\r\n\r\n\r\n```python        \r\n  A.b\r\n    default=[1,2,3]\r\n    bounds=(3,3)\r\n\r\n  B.b\r\n    default=[1,2,3]\r\n    bounds=(3,3)\r\n\r\n  C.b\r\n    default=10\r\n    bounds=(3,3) # I think; bounds will inherit after default is\r\n                   checked?\r\n```\r\n\r\nNotes:\r\n\r\n  * Shouldn't be possible to declare C.b as a Number because Number's\r\n    not a subclass of List.\r\n    \r\n  * (Ignoring the above,) C.b shouldn't be able to have a default\r\n    outside the bounds. (Default values be checked after bounds are\r\n    inherited).\r\n    \r\n\r\n"}}