Repository: sunpy/sunpy

Issue:
Consider not deriving HeliographicCarrington from HeliographicStonyhurst
Currently, `HeliographicCarrington` (HGC) is defined as a subclass of `HeliographicStonyhurst` (HGS), which is a straightforward way to avoid some code repetition.  However, this has the potentially surprising behavior that `isinstance()` cannot be used to check whether a frame is of a desired type: `isinstance(frame, HeliographicStonyhurst)` will return `True` even when `frame` is `HeliographicCarrington`.  `isinstance()` is used in this fashion within Astropy's `CoordinateAttribute` class, which we use for the `observer` attribute in our frames.

The solution is to define a new frame (e.g., `BaseHeliographic`) that has the common code but no defined transformations, and then have both HGS and HGC be subclasses of that new frame.  Since HGC isn't actually a version of HGS, having HGC be a subclass of HGS isn't really appropriate anyway.


Discussion:
I want to work on it. I found that `frames.py` file contains  class `SunPyBaseCoordinateFrame`, which is parent of HGS, so should we add the heliographic functionalities in `SunPyBaseCoordinateFrame` class itself, or create its child, `BaseHeliographic`, which will be inherited by HGS and HGC?
@abhijeetmanhas Great!  A new subclass of `SunPyBaseCoordinateFrame` should be created.  You can pretty much take the existing code for `HeliographicStonyhurst` and just rename it to `BaseHeliographic`.

Resolving patch:
diff --git a/sunpy/coordinates/frames.py b/sunpy/coordinates/frames.py
--- a/sunpy/coordinates/frames.py
+++ b/sunpy/coordinates/frames.py
@@ -136,8 +136,46 @@ def __str__(self):
             return super().__str__()
 
 
+class BaseHeliographic(SunPyBaseCoordinateFrame):
+    """
+    Base class for HeliographicCarrington (HGC) and HeliographicStonyhurst (HGS) frames.
+
+    This class is not intended to be used directly and has no transformations defined.
+    """
+    default_representation = SphericalRepresentation
+
+    frame_specific_representation_info = {
+        SphericalRepresentation: [RepresentationMapping(reprname='lon',
+                                                        framename='lon',
+                                                        defaultunit=u.deg),
+                                  RepresentationMapping(reprname='lat',
+                                                        framename='lat',
+                                                        defaultunit=u.deg),
+                                  RepresentationMapping(reprname='distance',
+                                                        framename='radius',
+                                                        defaultunit=None)],
+        CartesianRepresentation: [RepresentationMapping(reprname='x',
+                                                        framename='x'),
+                                  RepresentationMapping(reprname='y',
+                                                        framename='y'),
+                                  RepresentationMapping(reprname='z',
+                                                        framename='z')]
+    }
+
+    def __init__(self, *args, **kwargs):
+        _rep_kwarg = kwargs.get('representation_type', None)
+
+        super().__init__(*args, **kwargs)
+
+        # Make 3D if specified as 2D
+        if (self._data is not None and self._data.norm().unit is u.one
+            and u.allclose(self._data.norm(), 1*u.one)):
+
+            self._data *= _RSUN.to(u.km)
+
+
 @add_common_docstring(**_frame_parameters())
-class HeliographicStonyhurst(SunPyBaseCoordinateFrame):
+class HeliographicStonyhurst(BaseHeliographic):
     """
     A coordinate or frame in the Stonyhurst Heliographic (HGS) system.
 
@@ -195,40 +233,10 @@ class HeliographicStonyhurst(SunPyBaseCoordinateFrame):
     ``rsun``.
     """
     name = "heliographic_stonyhurst"
-    default_representation = SphericalRepresentation
-
-    frame_specific_representation_info = {
-        SphericalRepresentation: [RepresentationMapping(reprname='lon',
-                                                        framename='lon',
-                                                        defaultunit=u.deg),
-                                  RepresentationMapping(reprname='lat',
-                                                        framename='lat',
-                                                        defaultunit=u.deg),
-                                  RepresentationMapping(reprname='distance',
-                                                        framename='radius',
-                                                        defaultunit=None)],
-        CartesianRepresentation: [RepresentationMapping(reprname='x',
-                                                        framename='x'),
-                                  RepresentationMapping(reprname='y',
-                                                        framename='y'),
-                                  RepresentationMapping(reprname='z',
-                                                        framename='z')]
-    }
-
-    def __init__(self, *args, **kwargs):
-        _rep_kwarg = kwargs.get('representation_type', None)
-
-        super().__init__(*args, **kwargs)
-
-        # Make 3D if specified as 2D
-        if (self._data is not None and self._data.norm().unit is u.one
-            and u.allclose(self._data.norm(), 1*u.one)):
-
-            self._data *= _RSUN.to(u.km)
 
 
 @add_common_docstring(**_frame_parameters())
-class HeliographicCarrington(HeliographicStonyhurst):
+class HeliographicCarrington(BaseHeliographic):
     """
     A coordinate or frame in the Carrington Heliographic (HGC) system.
 


Test patch: