Repository: microsoft/Qcodes

Issue:
Inconsistent naming of parameters and instruments
This is not so much a bug as an annoying inconsistency. I am not sure if there is a good way to fix this whire retaining back wards compatibility but none the less the naming is inconsistent consider the following:


| type       | name                                       | full_name                | short_name        | `__str__` |
|------------|--------------------------------------------|--------------------------|-------------------|-------|
| Instrument | "`{name`}" e.g. "`myinst`"                       | N/A                      | N/A               | "`<{inst_class}: {name}>`" e.g "`<DummyInstrument: myinst>`" |
| Channel    |  "`...{parent_name}_{name}`" e.g. "`myinst_ch1`" | N/A                      | "`{name}`" e.g. "`ch1`" | "`<{channel_class}: ..._{parent_name}_{name} of {inst_class}: {inst_name}>`" e.g. <`MyChannel: myinst_ch1 of DummyInstrument: myinst`> |
| Parameter  | "{name}" e.g. "frequency"                  | "{instrument_name}_{name}" e.g. "myinst_ch1_frequency" | N/A               |  "`{instrument_name}_{name}`" e.g. "`myinst_ch1_frequency`"

It's inconsistent if name includes the full name or only the top most part. Depending on which class you are looking at you may have to use either full_name or shore_name instead to get what you are after. 
Inconsistent naming of parameters and instruments
This is not so much a bug as an annoying inconsistency. I am not sure if there is a good way to fix this whire retaining back wards compatibility but none the less the naming is inconsistent consider the following:


| type       | name                                       | full_name                | short_name        | `__str__` |
|------------|--------------------------------------------|--------------------------|-------------------|-------|
| Instrument | "`{name`}" e.g. "`myinst`"                       | N/A                      | N/A               | "`<{inst_class}: {name}>`" e.g "`<DummyInstrument: myinst>`" |
| Channel    |  "`...{parent_name}_{name}`" e.g. "`myinst_ch1`" | N/A                      | "`{name}`" e.g. "`ch1`" | "`<{channel_class}: ..._{parent_name}_{name} of {inst_class}: {inst_name}>`" e.g. <`MyChannel: myinst_ch1 of DummyInstrument: myinst`> |
| Parameter  | "{name}" e.g. "frequency"                  | "{instrument_name}_{name}" e.g. "myinst_ch1_frequency" | N/A               |  "`{instrument_name}_{name}`" e.g. "`myinst_ch1_frequency`"

It's inconsistent if name includes the full name or only the top most part. Depending on which class you are looking at you may have to use either full_name or shore_name instead to get what you are after. 


Discussion:
We could make this better by always implementing `short_name` and `full_name`:

* For Instrument they would all be the same 
* For Channels `name` and `full_name` would be the same
* For Parameter `name` and `short_name` would be the same
@QCoDeS/core 
Thanks for bringing this up. I have been annoyed by this confusion too.

I like your suggestion. What should `__str__` return?
I added the current behavior of `__str__` to the table too 
IMHO we should just use `__str__` as a human readable repr and not rely on any particular format of that
Thank you @jenshnielsen , this has also been bugging me for some time. I like your suggestions. In an ideal world I would want to only have `full_name` and `name`  where `name` is what you suggest to be `short_name`. But I think your suggestion is a good way to keep compatibility.
We could consider deprecating `name` to establish a less confusing situation in the future.
I agree that `__str__` should be the full human readable version and not be used for logic. So ideally we  implement the parameter __str__ in the style of the channel version (e.g. parallel to `full_name`)
@Dominik-Vogel sounds good to me

I also considered implementing something like a `name_parts` function that returns all the elements of the name in a list. For the parameter grouping it is useful to be able to get a name like channel_parameter but without the full instrument name included
We could make this better by always implementing `short_name` and `full_name`:

* For Instrument they would all be the same 
* For Channels `name` and `full_name` would be the same
* For Parameter `name` and `short_name` would be the same
@QCoDeS/core 
Thanks for bringing this up. I have been annoyed by this confusion too.

I like your suggestion. What should `__str__` return?
I added the current behavior of `__str__` to the table too 
IMHO we should just use `__str__` as a human readable repr and not rely on any particular format of that
Thank you @jenshnielsen , this has also been bugging me for some time. I like your suggestions. In an ideal world I would want to only have `full_name` and `name`  where `name` is what you suggest to be `short_name`. But I think your suggestion is a good way to keep compatibility.
We could consider deprecating `name` to establish a less confusing situation in the future.
I agree that `__str__` should be the full human readable version and not be used for logic. So ideally we  implement the parameter __str__ in the style of the channel version (e.g. parallel to `full_name`)
@Dominik-Vogel sounds good to me

I also considered implementing something like a `name_parts` function that returns all the elements of the name in a list. For the parameter grouping it is useful to be able to get a name like channel_parameter but without the full instrument name included

Resolving patch:
diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py
--- a/qcodes/instrument/base.py
+++ b/qcodes/instrument/base.py
@@ -45,6 +45,7 @@ class InstrumentBase(Metadatable, DelegateAttributes):
     def __init__(self, name: str,
                  metadata: Optional[Dict]=None, **kwargs) -> None:
         self.name = str(name)
+        self.short_name = str(name)
 
         self.parameters = {} # type: Dict[str, _BaseParameter]
         self.functions = {} # type: Dict[str, Function]
@@ -249,9 +250,26 @@ def print_readable_snapshot(self, update: bool=False,
             else:
                 submodule.print_readable_snapshot(update, max_chars)
 
+    @property
+    def parent(self):
+        """
+        Returns the parent instrument. By default this is None
+        Any SubInstrument should subclass this to return the parent instrument.
+        """
+        return None
+
     @property
     def root_instrument(self) -> 'InstrumentBase':
         return self
+
+    @property
+    def name_parts(self) -> List[str]:
+        name_parts = [self.short_name]
+        return name_parts
+
+    @property
+    def full_name(self):
+        return "_".join(self.name_parts)
     #
     # shortcuts to parameters & setters & getters                           #
     #
diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py
--- a/qcodes/instrument/channel.py
+++ b/qcodes/instrument/channel.py
@@ -59,10 +59,20 @@ def ask(self, cmd):
     def ask_raw(self, cmd):
         return self._parent.ask_raw(cmd)
 
+    @property
+    def parent(self) -> InstrumentBase:
+        return self._parent
+
     @property
     def root_instrument(self) -> InstrumentBase:
         return self._parent.root_instrument
 
+    @property
+    def name_parts(self) -> List[str]:
+        name_parts = self._parent.name_parts
+        name_parts.append(self.short_name)
+        return name_parts
+
 class MultiChannelInstrumentParameter(MultiParameter):
     """
     Parameter to get or set multiple channels simultaneously.
diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py
--- a/qcodes/instrument/parameter.py
+++ b/qcodes/instrument/parameter.py
@@ -200,6 +200,7 @@ def __init__(self, name: str,
                  delay: Optional[Union[int, float]]=None) -> None:
         super().__init__(metadata)
         self.name = str(name)
+        self.short_name = str(name)
         self._instrument = instrument
         self._snapshot_get = snapshot_get
         self._snapshot_value = snapshot_value
@@ -660,12 +661,7 @@ def inter_delay(self, inter_delay):
     # Deprecated
     @property
     def full_name(self):
-#        This can fully be replaced by str(parameter) in the future we
-#        may want to deprecate this but the current dataset makes heavy use
-#        of it in more complicated ways so keep it for now.
-#        warnings.warn('Attribute `full_name` is deprecated, please use '
-#                      'str(parameter)')
-        return str(self)
+        return "_".join(self.name_parts)
 
     def set_validator(self, vals):
         """
@@ -719,6 +715,16 @@ def set_to(self, value):
         self.set(value)
         return context_manager
 
+    @property
+    def name_parts(self) -> List[str]:
+        if self.instrument is not None:
+            name_parts = self.instrument.name_parts
+        else:
+            name_parts = []
+
+        name_parts.append(self.short_name)
+        return name_parts
+
 
 class Parameter(_BaseParameter):
     """


Test patch:
diff --git a/qcodes/tests/test_channels.py b/qcodes/tests/test_channels.py
--- a/qcodes/tests/test_channels.py
+++ b/qcodes/tests/test_channels.py
@@ -155,6 +155,53 @@ def test_access_channels_by_tuple(self, myindexs):
             assert chan.name == f'testchanneldummy_Chan{names[chanindex]}'
 
 
+    def test_names(self):
+        ex_inst_name = 'testchanneldummy'
+        for channel in self.instrument.channels:
+            sub_channel = DummyChannel(channel, 'subchannel', 'subchannel')
+            channel.add_submodule('somesubchannel', sub_channel)
+        assert self.instrument.name == ex_inst_name
+        assert self.instrument.full_name == ex_inst_name
+        assert self.instrument.short_name == ex_inst_name
+        assert self.instrument.name_parts == [ex_inst_name]
+
+        # Parameters directly on instrument
+        assert self.instrument.IDN.name == 'IDN'
+        assert self.instrument.IDN.full_name == f"{ex_inst_name}_IDN"
+        for chan, name in zip(self.instrument.channels,
+                              ['A', 'B', 'C', 'D', 'E', 'F']):
+            ex_chan_name = f"Chan{name}"
+            ex_chan_full_name = f"{ex_inst_name}_{ex_chan_name}"
+
+            assert chan.short_name == ex_chan_name
+            assert chan.name == ex_chan_full_name
+            assert chan.full_name == ex_chan_full_name
+            assert chan.name_parts == [ex_inst_name, ex_chan_name]
+
+            ex_param_name = 'temperature'
+            assert chan.temperature.name == ex_param_name
+            assert chan.temperature.full_name == f'{ex_chan_full_name}_{ex_param_name}'
+            assert chan.temperature.short_name == ex_param_name
+            assert chan.temperature.name_parts == [ex_inst_name, ex_chan_name,
+                                                   ex_param_name]
+
+            ex_subchan_name = f"subchannel"
+            ex_subchan_full_name = f"{ex_chan_full_name}_{ex_subchan_name}"
+
+            assert chan.somesubchannel.short_name == ex_subchan_name
+            assert chan.somesubchannel.name == ex_subchan_full_name
+            assert chan.somesubchannel.full_name == ex_subchan_full_name
+            assert chan.somesubchannel.name_parts == [ex_inst_name,
+                                                      ex_chan_name,
+                                                      ex_subchan_name]
+
+            assert chan.somesubchannel.temperature.name == ex_param_name
+            assert chan.somesubchannel.temperature.full_name == f'{ex_subchan_full_name}_{ex_param_name}'
+            assert chan.somesubchannel.temperature.short_name == ex_param_name
+            assert chan.somesubchannel.temperature.name_parts == [ex_inst_name,
+                                                                  ex_chan_name,
+                                                                  ex_subchan_name,
+                                                                  ex_param_name]
 class TestChannelsLoop(TestCase):
 
     def setUp(self):