Repository: Zeroto521/my-data-toolkit

Issue:
EHN: let `register_*_method` support alias feature
<!--
Thanks for contributing a pull request!

Please follow these standard acronyms to start the commit message:

- ENH: enhancement
- BUG: bug fix
- DOC: documentation
- TYP: type annotations
- TST: addition or modification of tests
- MAINT: maintenance commit (refactoring, typos, etc.)
- BLD: change related to building
- REL: related to releasing
- API: an (incompatible) API change
- DEP: deprecate something, or remove a deprecated object
- DEV: development tool or utility
- REV: revert an earlier commit
- PERF: performance improvement
- BOT: always commit via a bot
- CI: related to CI or CD
- CLN: Code cleanup
-->

- [x] closes #379 and #282
- [x] whatsnew entry

This could use accessor easier, e.g. `Series.lens` -> `Series.len`, `Series.get_attr` -> `Series.getattr`.
Before this PR, `len` is a python keyword, so it can't use `len` as the method name.
This PR will cause v0.0.9 version delay.


Discussion:

Resolving patch:
diff --git a/dtoolkit/accessor/series.py b/dtoolkit/accessor/series.py
--- a/dtoolkit/accessor/series.py
+++ b/dtoolkit/accessor/series.py
@@ -348,7 +348,7 @@ def wrap_collapse(x) -> list[Any]:
         return [x]
 
     s_list = s.apply(wrap_collapse)
-    s_len = s_list.lens()
+    s_len = s_list.len()
     if all(s_len == 1):
         return s
 
@@ -369,9 +369,15 @@ def wrap_collapse(x) -> list[Any]:
     ).add_prefix(s.name + delimiter)
 
 
+@register_series_method(name="len")
 @register_series_method
 def lens(s: pd.Series) -> pd.Series:
     """
+    .. warning::
+
+        Call this method via ``Series.lens()`` is deprecated and will be removed
+        in **0.0.10**. Please call it via ``Series.len()`` instead.
+
     Return the length of each element in the series.
 
     Equals to::
@@ -382,6 +388,11 @@ def lens(s: pd.Series) -> pd.Series:
     -------
     Series
 
+    Notes
+    -----
+    To keep the Python naming style, so use this accessor via
+    ``Series.len`` not ``Series.lens``.
+
     Examples
     --------
     >>> import dtoolkit.accessor
@@ -396,7 +407,7 @@ def lens(s: pd.Series) -> pd.Series:
     5                  {}
     6    <class 'object'>
     dtype: object
-    >>> s.lens()
+    >>> s.len()
     0    1.0
     1    1.0
     2    6.0
@@ -406,6 +417,16 @@ def lens(s: pd.Series) -> pd.Series:
     6    NaN
     dtype: float64
     """
+    from warnings import warn
+
+    warn(
+        "Call 'dtoolkit.accessor.series.lens' method via `Series.lens()` "
+        "is deprecated and will be removed in 0.0.10. Please call it via "
+        "'Series.len()' instead. "
+        "(Warning added DToolKit 0.0.9)",
+        DeprecationWarning,
+    )
+
     from pandas.api.types import is_number
 
     def wrap_len(x) -> int | None:
@@ -511,7 +532,7 @@ def error_report(
     )
 
 
-@register_series_method
+@register_series_method(name="getattr")
 def get_attr(s: pd.Series, name: str, *args, **kwargs) -> pd.Series:
     """
     Return the value of the named attribute of Series element.
@@ -534,6 +555,11 @@ def get_attr(s: pd.Series, name: str, *args, **kwargs) -> pd.Series:
     --------
     getattr
 
+    Notes
+    -----
+    To keep the Python naming style, so use this accessor via
+    ``Series.getattr`` not ``Series.get_attr``.
+
     Examples
     --------
     >>> import dtoolkit.accessor
@@ -542,21 +568,21 @@ def get_attr(s: pd.Series, name: str, *args, **kwargs) -> pd.Series:
 
     Get a attribute.
 
-    >>> s.get_attr("__doc__")
+    >>> s.getattr("__doc__")
     0    str(object='') -> str\\nstr(bytes_or_buffer[, e...
     1    str(object='') -> str\\nstr(bytes_or_buffer[, e...
     dtype: object
 
     Get a don't exist attribute.
 
-    >>> s.get_attr("whatever")
+    >>> s.getattr("whatever")
     0    None
     1    None
     dtype: object
 
     Get a method attribute and call it.
 
-    >>> s.get_attr("count", "l")
+    >>> s.getattr("count", "l")
     0    2
     1    1
     dtype: int64
diff --git a/dtoolkit/geoaccessor/geoseries.py b/dtoolkit/geoaccessor/geoseries.py
--- a/dtoolkit/geoaccessor/geoseries.py
+++ b/dtoolkit/geoaccessor/geoseries.py
@@ -98,7 +98,7 @@ def geobuffer(
     elif not is_number(distance):
         raise TypeError("type of 'distance' should be int or float.")
 
-    utms = s.utm_crs().get_attr("code").to_numpy()
+    utms = s.utm_crs().getattr("code").to_numpy()
 
     s_index = s.index
     s = s.reset_index(drop=True)
@@ -285,7 +285,7 @@ def utm_crs(s: gpd.GeoSeries, datum_name: str = "WGS 84") -> pd.Series:
 
     Get the EPSG code.
 
-    >>> s.utm_crs().get_attr("code")
+    >>> s.utm_crs().getattr("code")
     0    32650
     1    32647
     dtype: object


Test patch:
diff --git a/test/accessor/test_series.py b/test/accessor/test_series.py
--- a/test/accessor/test_series.py
+++ b/test/accessor/test_series.py
@@ -535,6 +535,6 @@ class TestGetAttr:
         ],
     )
     def test_attr_work(self, s, name, args, kwargs, excepted):
-        result = s.get_attr(name, *args, **kwargs)
+        result = s.getattr(name, *args, **kwargs)
 
         assert result.equals(excepted)