Repository: narwhals-dev/narwhals
Pull request #459 (2024-07-09 14:33:27)

chore: rename some internal attributes for clarity
<!--
# Thanks for contributing a pull request! 
## Please make sure you see our contribution guidelines: https://github.com/narwhals-dev/narwhals/blob/main/CONTRIBUTING.md
-->

## What type of PR is this? (check all applicable)

- [ ] 💾 Refactor
- [ ] ✨ Feature
- [ ] 🐛 Bug Fix
- [ ] 🔧 Optimization
- [ ] 📝 Documentation
- [ ] ✅ Test
- [ ] 🐳 Other

## Related issues 

- Related issue # 
- Closes #

## Checklist

- [ ] Code follows style guide (ruff)
- [ ] Tests added 
- [ ] Documented the changes

## If you have comments or can explain your changes, please do so below.




Patch:
diff --git a/docs/how_it_works.md b/docs/how_it_works.md
index e6a46371..85838ddd 100644
--- a/docs/how_it_works.md
+++ b/docs/how_it_works.md
@@ -11,7 +11,7 @@ order to make sense of Narwhals:
 For example, `nw.col('a')` means "given a dataframe `df`, give me the Series `'a'` from `df`".
 Translating this to pandas syntax, we get:
 
-```python
+```python exec="1" source="above"
 def col_a(df):
     return [df.loc[:, 'a']]
 ```
@@ -19,7 +19,7 @@ def col_a(df):
 Let's step up the complexity. How about `nw.col('a')+1`? We already know what the
 `nw.col('a')` part looks like, so we just need to add `1` to each of its outputs:
 
-```python
+```python exec="1"
 def col_a(df):
     return [df.loc[:, 'a']]
 
@@ -29,7 +29,7 @@ def col_a_plus_1(df):
 
 Expressions can return multiple Series - for example, `nw.col('a', 'b')` translates to:
 
-```python
+```python exec="1"
 def col_a_b(df):
     return [df.loc[:, 'a'], df.loc[:, 'b']]
 ```
@@ -37,7 +37,7 @@ def col_a_b(df):
 Expressions can also take multiple columns as input - for example, `nw.sum_horizontal('a', 'b')`
 translates to:
 
-```python
+```python exec="1"
 def sum_horizontal_a_b(df):
     return [df.loc[:, 'a'] + df.loc[:, 'b']]
 ```
@@ -61,7 +61,7 @@ Now let's turn our attention to the implementation.
 ## Polars implementation
 
 For Polars, Narwhals just "passes everything through". For example consider the following:
-```python
+```python exec="1"
 import polars as pl
 import narwhals as nw
 
@@ -84,15 +84,21 @@ The pandas namespace (`pd`) isn't Narwhals-compliant, as the pandas API is very
 from Polars'. So...Narwhals implements a `PandasNamespace`, which includes the top-level
 Polars functions included in the Narwhals API:
 
-```python
+```python exec="1" source="above", result="python" session="pandas_impl"
+import pandas as pd
 import narwhals as nw
 from narwhals._pandas_like.namespace import PandasNamespace
-
-pn = PandasNamespace(implementation='pandas')
-nw.col('a')._call(pn)
+from narwhals._pandas_like.utils import Implementation
+from narwhals.utils import parse_version
+
+pn = PandasNamespace(
+    implementation=Implementation.PANDAS,
+    backend_version=parse_version(pd.__version__),
+)
+print(nw.col('a')._call(pn))
 ```
 The result from the last line above is the same as we'd get from `pn.col('a')`, and it's
-a `narwhals._pandas_like.expression.PandasExpr` object, which we'll call `PandasExpr` for
+a `narwhals._pandas_like.expr.PandasExpr` object, which we'll call `PandasExpr` for
 short.
 
 `PandasExpr` also has a `_call` method - but this one expects a `PandasDataFrame` as input.
@@ -101,28 +107,32 @@ The `_call` method gives us that function! Let's see it in action.
 
 Note: the following examples use `PandasDataFrame` and `PandasSeries`. These are backed
 by actual `pandas.DataFrame`s and `pandas.Series` respectively and are Narwhals-compliant. We can access the 
-underlying pandas objects via `PandasDataFrame._dataframe` and `PandasSeries._series`.
+underlying pandas objects via `PandasDataFrame._native_dataframe` and `PandasSeries._native_series`.
 
-```python
+```python exec="1" result="python" session="pandas_impl" source="above"
 import narwhals as nw
 from narwhals._pandas_like.namespace import PandasNamespace
+from narwhals._pandas_like.utils import Implementation
 from narwhals._pandas_like.dataframe import PandasDataFrame
+from narwhals.utils import parse_version
 import pandas as pd
 
-pn = PandasNamespace(implementation='pandas')
+pn = PandasNamespace(
+    implementation=Implementation.PANDAS,
+    backend_version=parse_version(pd.__version__),
+)
 
 df_pd = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]})
-df = PandasDataFrame(df_pd, implementation='pandas')
+df = PandasDataFrame(
+    df_pd,
+    implementation=Implementation.PANDAS,
+    backend_version=parse_version(pd.__version__),
+)
 expression = pn.col('a') + 1
 result = expression._call(df)
-print([x._series for x in result])
-```
-The first (and only) Series to be output is:
-```
-0    2
-1    3
-2    4
-Name: a, dtype: int64
+print(f'length of result: {len(result)}\n')
+print('native series of first value of result: ')
+print([x._native_series for x in result][0])
 ```
 
 So indeed, our expression did what it said on the tin - it took some dataframe, took
@@ -167,13 +177,10 @@ In Narwhals, here's what we do:
 
 In order to tell whether an aggregation is simple, Narwhals uses the private `_depth` attribute of `PandasExpr`:
 
-```python
->>> pn.col('a').mean()
-PandasExpr(depth=1, function_name=col->mean, root_names=['a'], output_names=['a']
->>> (pn.col('a')+1).mean()
-PandasExpr(depth=2, function_name=col->__add__->mean, root_names=['a'], output_names=['a']
->>> pn.mean('a')
-PandasExpr(depth=1, function_name=col->mean, root_names=['a'], output_names=['a']
+```python exec="1" result="python" session="pandas_impl" source="above"
+print(pn.col('a').mean())
+print((pn.col('a')+1).mean())
+print(pn.mean('a'))
 ```
 
 For simple aggregations, Narwhals can just look at `_depth` and `function_name` and figure out
diff --git a/narwhals/_arrow/series.py b/narwhals/_arrow/series.py
index 9befa6c1..537be798 100644
--- a/narwhals/_arrow/series.py
+++ b/narwhals/_arrow/series.py
@@ -163,7 +163,7 @@ class ArrowSeries:
 
 class ArrowSeriesDateTimeNamespace:
     def __init__(self, series: ArrowSeries) -> None:
-        self._series = series
+        self._arrow_series = series
 
     def to_string(self, format: str) -> ArrowSeries:  # noqa: A002
         pc = get_pyarrow_compute()
@@ -171,37 +171,37 @@ class ArrowSeriesDateTimeNamespace:
         # the fractional part of the second...:'(
         # https://arrow.apache.org/docs/python/generated/pyarrow.compute.strftime.html
         format = format.replace("%S.%f", "%S").replace("%S%.f", "%S")
-        return self._series._from_native_series(
-            pc.strftime(self._series._native_series, format)
+        return self._arrow_series._from_native_series(
+            pc.strftime(self._arrow_series._native_series, format)
         )
 
 
 class ArrowSeriesCatNamespace:
     def __init__(self, series: ArrowSeries) -> None:
-        self._series = series
+        self._arrow_series = series
 
     def get_categories(self) -> ArrowSeries:
         pa = get_pyarrow()
-        ca = self._series._native_series
+        ca = self._arrow_series._native_series
         # TODO(Unassigned): this looks potentially expensive - is there no better way?
         out = pa.chunked_array(
             [pa.concat_arrays([x.dictionary for x in ca.chunks]).unique()]
         )
-        return self._series._from_native_series(out)
+        return self._arrow_series._from_native_series(out)
 
 
 class ArrowSeriesStringNamespace:
     def __init__(self, series: ArrowSeries) -> None:
-        self._series = series
+        self._arrow_series = series
 
     def to_uppercase(self) -> ArrowSeries:
         pc = get_pyarrow_compute()
-        return self._series._from_native_series(
-            pc.utf8_upper(self._series._native_series),
+        return self._arrow_series._from_native_series(
+            pc.utf8_upper(self._arrow_series._native_series),
         )
 
     def to_lowercase(self) -> ArrowSeries:
         pc = get_pyarrow_compute()
-        return self._series._from_native_series(
-            pc.utf8_lower(self._series._native_series),
+        return self._arrow_series._from_native_series(
+            pc.utf8_lower(self._arrow_series._native_series),
         )
diff --git a/narwhals/_pandas_like/series.py b/narwhals/_pandas_like/series.py
index 618ede34..f4a8acfb 100644
--- a/narwhals/_pandas_like/series.py
+++ b/narwhals/_pandas_like/series.py
@@ -586,102 +586,104 @@ class PandasSeries:
 
 class PandasSeriesCatNamespace:
     def __init__(self, series: PandasSeries) -> None:
-        self._series = series
+        self._pandas_series = series
 
     def get_categories(self) -> PandasSeries:
-        s = self._series._native_series
-        return self._series._from_native_series(
+        s = self._pandas_series._native_series
+        return self._pandas_series._from_native_series(
             s.__class__(s.cat.categories, name=s.name)
         )
 
 
 class PandasSeriesStringNamespace:
     def __init__(self, series: PandasSeries) -> None:
-        self._series = series
+        self._pandas_series = series
 
     def starts_with(self, prefix: str) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.str.startswith(prefix),
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.startswith(prefix),
         )
 
     def ends_with(self, suffix: str) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.str.endswith(suffix),
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.endswith(suffix),
         )
 
     def contains(self, pattern: str, *, literal: bool = False) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.str.contains(pat=pattern, regex=not literal)
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.contains(
+                pat=pattern, regex=not literal
+            )
         )
 
     def slice(self, offset: int, length: int | None = None) -> PandasSeries:
         stop = offset + length if length else None
-        return self._series._from_native_series(
-            self._series._native_series.str.slice(start=offset, stop=stop),
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.slice(start=offset, stop=stop),
         )
 
     def to_datetime(self, format: str | None = None) -> PandasSeries:  # noqa: A002
-        return self._series._from_native_series(
-            to_datetime(self._series._implementation)(
-                self._series._native_series, format=format
+        return self._pandas_series._from_native_series(
+            to_datetime(self._pandas_series._implementation)(
+                self._pandas_series._native_series, format=format
             )
         )
 
     def to_uppercase(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.str.upper(),
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.upper(),
         )
 
     def to_lowercase(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.str.lower(),
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.str.lower(),
         )
 
 
 class PandasSeriesDateTimeNamespace:
     def __init__(self, series: PandasSeries) -> None:
-        self._series = series
+        self._pandas_series = series
 
     def year(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.year,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.year,
         )
 
     def month(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.month,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.month,
         )
 
     def day(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.day,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.day,
         )
 
     def hour(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.hour,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.hour,
         )
 
     def minute(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.minute,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.minute,
         )
 
     def second(self) -> PandasSeries:
-        return self._series._from_native_series(
-            self._series._native_series.dt.second,
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.second,
         )
 
     def millisecond(self) -> PandasSeries:
         return self.microsecond() // 1000
 
     def microsecond(self) -> PandasSeries:
-        if self._series._backend_version < (3, 0, 0) and "pyarrow" in str(
-            self._series._native_series.dtype
+        if self._pandas_series._backend_version < (3, 0, 0) and "pyarrow" in str(
+            self._pandas_series._native_series.dtype
         ):
             # crazy workaround for https://github.com/pandas-dev/pandas/issues/59154
             pc = get_pyarrow_compute()
-            native_series = self._series._native_series
+            native_series = self._pandas_series._native_series
             arr = native_series.array.__arrow_array__()
             result_arr = pc.add(
                 pc.multiply(pc.millisecond(arr), 1000), pc.microsecond(arr)
@@ -689,87 +691,89 @@ class PandasSeriesDateTimeNamespace:
             result = native_series.__class__(
                 native_series.array.__class__(result_arr), name=native_series.name
             )
-            return self._series._from_native_series(result)
+            return self._pandas_series._from_native_series(result)
 
-        return self._series._from_native_series(
-            self._series._native_series.dt.microsecond
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.microsecond
         )
 
     def nanosecond(self) -> PandasSeries:
-        return self.microsecond() * 1_000 + self._series._native_series.dt.nanosecond  # type: ignore[no-any-return]
+        return (  # type: ignore[no-any-return]
+            self.microsecond() * 1_000 + self._pandas_series._native_series.dt.nanosecond
+        )
 
     def ordinal_day(self) -> PandasSeries:
-        ser = self._series._native_series
+        ser = self._pandas_series._native_series
         year_start = ser.dt.year
         result = (
             ser.to_numpy().astype("datetime64[D]")
             - (year_start.to_numpy() - 1970).astype("datetime64[Y]")
         ).astype("int32") + 1
         dtype = "Int64[pyarrow]" if "pyarrow" in str(ser.dtype) else "int32"
-        return self._series._from_native_series(
-            self._series._native_series.__class__(
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.__class__(
                 result, dtype=dtype, name=year_start.name
             )
         )
 
     def total_minutes(self) -> PandasSeries:
-        s = self._series._native_series.dt.total_seconds()
+        s = self._pandas_series._native_series.dt.total_seconds()
         s_sign = (
             2 * (s > 0).astype(int_dtype_mapper(s.dtype)) - 1
         )  # this calculates the sign of each series element
         s_abs = s.abs() // 60
         if ~s.isna().any():
             s_abs = s_abs.astype(int_dtype_mapper(s.dtype))
-        return self._series._from_native_series(s_abs * s_sign)
+        return self._pandas_series._from_native_series(s_abs * s_sign)
 
     def total_seconds(self) -> PandasSeries:
-        s = self._series._native_series.dt.total_seconds()
+        s = self._pandas_series._native_series.dt.total_seconds()
         s_sign = (
             2 * (s > 0).astype(int_dtype_mapper(s.dtype)) - 1
         )  # this calculates the sign of each series element
         s_abs = s.abs() // 1
         if ~s.isna().any():
             s_abs = s_abs.astype(int_dtype_mapper(s.dtype))
-        return self._series._from_native_series(s_abs * s_sign)
+        return self._pandas_series._from_native_series(s_abs * s_sign)
 
     def total_milliseconds(self) -> PandasSeries:
-        s = self._series._native_series.dt.total_seconds() * 1e3
+        s = self._pandas_series._native_series.dt.total_seconds() * 1e3
         s_sign = (
             2 * (s > 0).astype(int_dtype_mapper(s.dtype)) - 1
         )  # this calculates the sign of each series element
         s_abs = s.abs() // 1
         if ~s.isna().any():
             s_abs = s_abs.astype(int_dtype_mapper(s.dtype))
-        return self._series._from_native_series(s_abs * s_sign)
+        return self._pandas_series._from_native_series(s_abs * s_sign)
 
     def total_microseconds(self) -> PandasSeries:
-        s = self._series._native_series.dt.total_seconds() * 1e6
+        s = self._pandas_series._native_series.dt.total_seconds() * 1e6
         s_sign = (
             2 * (s > 0).astype(int_dtype_mapper(s.dtype)) - 1
         )  # this calculates the sign of each series element
         s_abs = s.abs() // 1
         if ~s.isna().any():
             s_abs = s_abs.astype(int_dtype_mapper(s.dtype))
-        return self._series._from_native_series(s_abs * s_sign)
+        return self._pandas_series._from_native_series(s_abs * s_sign)
 
     def total_nanoseconds(self) -> PandasSeries:
-        s = self._series._native_series.dt.total_seconds() * 1e9
+        s = self._pandas_series._native_series.dt.total_seconds() * 1e9
         s_sign = (
             2 * (s > 0).astype(int_dtype_mapper(s.dtype)) - 1
         )  # this calculates the sign of each series element
         s_abs = s.abs() // 1
         if ~s.isna().any():
             s_abs = s_abs.astype(int_dtype_mapper(s.dtype))
-        return self._series._from_native_series(s_abs * s_sign)
+        return self._pandas_series._from_native_series(s_abs * s_sign)
 
     def to_string(self, format: str) -> PandasSeries:  # noqa: A002
         # Polars' parser treats `'%.f'` as pandas does `'.%f'`
         # PyArrow interprets `'%S'` as "seconds, plus fractional seconds"
         # and doesn't support `%f`
-        if "pyarrow" not in str(self._series._native_series.dtype):
+        if "pyarrow" not in str(self._pandas_series._native_series.dtype):
             format = format.replace("%S%.f", "%S.%f")
         else:
             format = format.replace("%S.%f", "%S").replace("%S%.f", "%S")
-        return self._series._from_native_series(
-            self._series._native_series.dt.strftime(format)
+        return self._pandas_series._from_native_series(
+            self._pandas_series._native_series.dt.strftime(format)
         )
diff --git a/narwhals/dataframe.py b/narwhals/dataframe.py
index 4d07d4b4..d0cadc80 100644
--- a/narwhals/dataframe.py
+++ b/narwhals/dataframe.py
@@ -35,24 +35,24 @@ FrameT = TypeVar("FrameT", bound="IntoDataFrame")
 
 
 class BaseFrame(Generic[FrameT]):
-    _dataframe: Any
+    _compliant_frame: Any
     _is_polars: bool
     _backend_version: tuple[int, ...]
 
     def __len__(self) -> Any:
-        return self._dataframe.__len__()
+        return self._compliant_frame.__len__()
 
     def __native_namespace__(self) -> Any:
         if self._is_polars:
             return get_polars()
-        return self._dataframe.__native_namespace__()
+        return self._compliant_frame.__native_namespace__()
 
     def __narwhals_namespace__(self) -> Any:
         if self._is_polars:
             return get_polars()
-        return self._dataframe.__narwhals_namespace__()
+        return self._compliant_frame.__narwhals_namespace__()
 
-    def _from_dataframe(self, df: Any) -> Self:
+    def _from_compliant_dataframe(self, df: Any) -> Self:
         # construct, preserving properties
         return self.__class__(  # type: ignore[call-arg]
             df,
@@ -62,18 +62,18 @@ class BaseFrame(Generic[FrameT]):
 
     def _flatten_and_extract(self, *args: Any, **kwargs: Any) -> Any:
         """Process `args` and `kwargs`, extracting underlying objects as we go."""
-        args = [self._extract_native(v) for v in flatten(args)]  # type: ignore[assignment]
-        kwargs = {k: self._extract_native(v) for k, v in kwargs.items()}
+        args = [self._extract_compliant(v) for v in flatten(args)]  # type: ignore[assignment]
+        kwargs = {k: self._extract_compliant(v) for k, v in kwargs.items()}
         return args, kwargs
 
-    def _extract_native(self, arg: Any) -> Any:
+    def _extract_compliant(self, arg: Any) -> Any:
         from narwhals.expression import Expr
         from narwhals.series import Series
 
         if isinstance(arg, BaseFrame):
-            return arg._dataframe
+            return arg._compliant_frame
         if isinstance(arg, Series):
-            return arg._series
+            return arg._compliant_series
         if isinstance(arg, Expr):
             return arg._call(self.__narwhals_namespace__())
         if get_polars() is not None and "polars" in str(type(arg)):
@@ -90,7 +90,7 @@ class BaseFrame(Generic[FrameT]):
     def schema(self) -> dict[str, DType]:
         return {
             k: to_narwhals_dtype(v, is_polars=self._is_polars)
-            for k, v in self._dataframe.schema.items()
+            for k, v in self._compliant_frame.schema.items()
         }
 
     def pipe(self, function: Callable[[Any], Self], *args: Any, **kwargs: Any) -> Self:
@@ -98,25 +98,25 @@ class BaseFrame(Generic[FrameT]):
 
     def with_row_index(self, name: str = "index") -> Self:
         if self._is_polars and self._backend_version < (0, 20, 4):  # pragma: no cover
-            return self._from_dataframe(
-                self._dataframe.with_row_count(name),
+            return self._from_compliant_dataframe(
+                self._compliant_frame.with_row_count(name),
             )
-        return self._from_dataframe(
-            self._dataframe.with_row_index(name),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.with_row_index(name),
         )
 
     def drop_nulls(self) -> Self:
-        return self._from_dataframe(
-            self._dataframe.drop_nulls(),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.drop_nulls(),
         )
 
     @property
     def columns(self) -> list[str]:
-        return self._dataframe.columns  # type: ignore[no-any-return]
+        return self._compliant_frame.columns  # type: ignore[no-any-return]
 
     def lazy(self) -> LazyFrame[Any]:
         return LazyFrame(
-            self._dataframe.lazy(),
+            self._compliant_frame.lazy(),
             is_polars=self._is_polars,
             backend_version=self._backend_version,
         )
@@ -125,8 +125,8 @@ class BaseFrame(Generic[FrameT]):
         self, *exprs: IntoExpr | Iterable[IntoExpr], **named_exprs: IntoExpr
     ) -> Self:
         exprs, named_exprs = self._flatten_and_extract(*exprs, **named_exprs)
-        return self._from_dataframe(
-            self._dataframe.with_columns(*exprs, **named_exprs),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.with_columns(*exprs, **named_exprs),
         )
 
     def select(
@@ -135,29 +135,29 @@ class BaseFrame(Generic[FrameT]):
         **named_exprs: IntoExpr,
     ) -> Self:
         exprs, named_exprs = self._flatten_and_extract(*exprs, **named_exprs)
-        return self._from_dataframe(
-            self._dataframe.select(*exprs, **named_exprs),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.select(*exprs, **named_exprs),
         )
 
     def rename(self, mapping: dict[str, str]) -> Self:
-        return self._from_dataframe(self._dataframe.rename(mapping))
+        return self._from_compliant_dataframe(self._compliant_frame.rename(mapping))
 
     def head(self, n: int) -> Self:
-        return self._from_dataframe(self._dataframe.head(n))
+        return self._from_compliant_dataframe(self._compliant_frame.head(n))
 
     def tail(self, n: int) -> Self:
-        return self._from_dataframe(self._dataframe.tail(n))
+        return self._from_compliant_dataframe(self._compliant_frame.tail(n))
 
     def drop(self, *columns: str | Iterable[str]) -> Self:
-        return self._from_dataframe(self._dataframe.drop(*columns))
+        return self._from_compliant_dataframe(self._compliant_frame.drop(*columns))
 
     def unique(self, subset: str | list[str]) -> Self:
-        return self._from_dataframe(self._dataframe.unique(subset=subset))
+        return self._from_compliant_dataframe(self._compliant_frame.unique(subset=subset))
 
     def filter(self, *predicates: IntoExpr | Iterable[IntoExpr]) -> Self:
         predicates, _ = self._flatten_and_extract(*predicates)
-        return self._from_dataframe(
-            self._dataframe.filter(*predicates),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.filter(*predicates),
         )
 
     def sort(
@@ -166,8 +166,8 @@ class BaseFrame(Generic[FrameT]):
         *more_by: str,
         descending: bool | Sequence[bool] = False,
     ) -> Self:
-        return self._from_dataframe(
-            self._dataframe.sort(by, *more_by, descending=descending)
+        return self._from_compliant_dataframe(
+            self._compliant_frame.sort(by, *more_by, descending=descending)
         )
 
     def join(
@@ -189,9 +189,9 @@ class BaseFrame(Generic[FrameT]):
             raise ValueError(msg)
 
         validate_same_library([self, other])
-        return self._from_dataframe(
-            self._dataframe.join(
-                self._extract_native(other),
+        return self._from_compliant_dataframe(
+            self._compliant_frame.join(
+                self._extract_compliant(other),
                 how=how,
                 left_on=left_on,
                 right_on=right_on,
@@ -199,7 +199,7 @@ class BaseFrame(Generic[FrameT]):
         )
 
     def clone(self) -> Self:
-        return self._from_dataframe(self._dataframe.clone())
+        return self._from_compliant_dataframe(self._compliant_frame.clone())
 
 
 class DataFrame(BaseFrame[FrameT]):
@@ -222,15 +222,15 @@ class DataFrame(BaseFrame[FrameT]):
         self._is_polars = is_polars
         self._backend_version = backend_version
         if hasattr(df, "__narwhals_dataframe__"):
-            self._dataframe: Any = df.__narwhals_dataframe__()
+            self._compliant_frame: Any = df.__narwhals_dataframe__()
         elif is_polars and isinstance(df, get_polars().DataFrame):
-            self._dataframe = df
+            self._compliant_frame = df
         else:
             msg = f"Expected polars DataFrame or object which implements `__narwhals_dataframe__`, got: {type(df)}"
             raise TypeError(msg)
 
     def __array__(self) -> np.ndarray:
-        return self._dataframe.to_numpy()
+        return self._compliant_frame.to_numpy()
 
     def __repr__(self) -> str:  # pragma: no cover
         header = " Narwhals DataFrame                            "
@@ -314,7 +314,7 @@ class DataFrame(BaseFrame[FrameT]):
             1    2  7.0   b
             2    3  8.0   c
         """
-        return self._dataframe.to_pandas()
+        return self._compliant_frame.to_pandas()
 
     def write_parquet(self, file: str | Path | BytesIO) -> Any:
         """
@@ -341,7 +341,7 @@ class DataFrame(BaseFrame[FrameT]):
             >>> func(df_pd)  # doctest:+SKIP
             >>> func(df_pl)  # doctest:+SKIP
         """
-        self._dataframe.write_parquet(file)
+        self._compliant_frame.write_parquet(file)
 
     def to_numpy(self) -> Any:
         """
@@ -375,7 +375,7 @@ class DataFrame(BaseFrame[FrameT]):
                    [2, 7.0, 'b'],
                    [3, 8.5, 'c']], dtype=object)
         """
-        return self._dataframe.to_numpy()
+        return self._compliant_frame.to_numpy()
 
     @property
     def shape(self) -> tuple[int, int]:
@@ -405,7 +405,7 @@ class DataFrame(BaseFrame[FrameT]):
             >>> func(df_pl)
             (5, 1)
         """
-        return self._dataframe.shape  # type: ignore[no-any-return]
+        return self._compliant_frame.shape  # type: ignore[no-any-return]
 
     @overload
     def __getitem__(self, item: Sequence[int]) -> Series: ...
@@ -421,7 +421,7 @@ class DataFrame(BaseFrame[FrameT]):
             from narwhals.series import Series
 
             return Series(
-                self._dataframe[item],
+                self._compliant_frame[item],
                 backend_version=self._backend_version,
                 is_polars=self._is_polars,
             )
@@ -431,7 +431,7 @@ class DataFrame(BaseFrame[FrameT]):
             and isinstance(item, np.ndarray)
             and item.ndim == 1
         ):
-            return self._from_dataframe(self._dataframe[item])
+            return self._from_compliant_dataframe(self._compliant_frame[item])
 
         else:
             msg = f"Expected str or slice, got: {type(item)}"
@@ -490,9 +490,11 @@ class DataFrame(BaseFrame[FrameT]):
                     backend_version=self._backend_version,
                     is_polars=self._is_polars,
                 )
-                for key, value in self._dataframe.to_dict(as_series=as_series).items()
+                for key, value in self._compliant_frame.to_dict(
+                    as_series=as_series
+                ).items()
             }
-        return self._dataframe.to_dict(as_series=as_series)  # type: ignore[no-any-return]
+        return self._compliant_frame.to_dict(as_series=as_series)  # type: ignore[no-any-return]
 
     # inherited
     def pipe(self, function: Callable[[Any], Self], *args: Any, **kwargs: Any) -> Self:
@@ -742,7 +744,7 @@ class DataFrame(BaseFrame[FrameT]):
             >>> func(df_pl, named=True)
             [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
         """
-        return self._dataframe.rows(named=named)  # type: ignore[no-any-return]
+        return self._compliant_frame.rows(named=named)  # type: ignore[no-any-return]
 
     @overload
     def iter_rows(
@@ -798,7 +800,7 @@ class DataFrame(BaseFrame[FrameT]):
             >>> [row for row in func(df_pl, named=True)]
             [{'foo': 1, 'bar': 6.0, 'ham': 'a'}, {'foo': 2, 'bar': 7.0, 'ham': 'b'}, {'foo': 3, 'bar': 8.0, 'ham': 'c'}]
         """
-        return self._dataframe.iter_rows(named=named, buffer_size=buffer_size)  # type: ignore[no-any-return]
+        return self._compliant_frame.iter_rows(named=named, buffer_size=buffer_size)  # type: ignore[no-any-return]
 
     def with_columns(
         self, *exprs: IntoExpr | Iterable[IntoExpr], **named_exprs: IntoExpr
@@ -1597,7 +1599,7 @@ class DataFrame(BaseFrame[FrameT]):
         from narwhals.series import Series
 
         return Series(
-            self._dataframe.is_duplicated(),
+            self._compliant_frame.is_duplicated(),
             backend_version=self._backend_version,
             is_polars=self._is_polars,
         )
@@ -1631,7 +1633,7 @@ class DataFrame(BaseFrame[FrameT]):
             (False, False)
         """
 
-        return self._dataframe.is_empty()  # type: ignore[no-any-return]
+        return self._compliant_frame.is_empty()  # type: ignore[no-any-return]
 
     def is_unique(self: Self) -> Series:
         r"""
@@ -1683,7 +1685,7 @@ class DataFrame(BaseFrame[FrameT]):
         from narwhals.series import Series
 
         return Series(
-            self._dataframe.is_unique(),
+            self._compliant_frame.is_unique(),
             backend_version=self._backend_version,
             is_polars=self._is_polars,
         )
@@ -1740,7 +1742,7 @@ class DataFrame(BaseFrame[FrameT]):
             └─────┴─────┴─────┘
         """
 
-        return self._from_dataframe(self._dataframe.null_count())
+        return self._from_compliant_dataframe(self._compliant_frame.null_count())
 
     def item(self: Self, row: int | None = None, column: int | str | None = None) -> Any:
         r"""
@@ -1772,7 +1774,7 @@ class DataFrame(BaseFrame[FrameT]):
             >>> func(df_pl, 1, 1), func(df_pl, 2, "b")
             (5, 6)
         """
-        return self._dataframe.item(row=row, column=column)
+        return self._compliant_frame.item(row=row, column=column)
 
     def clone(self) -> Self:
         r"""
@@ -1831,11 +1833,11 @@ class LazyFrame(BaseFrame[FrameT]):
         self._is_polars = is_polars
         self._backend_version = backend_version
         if hasattr(df, "__narwhals_lazyframe__"):
-            self._dataframe: Any = df.__narwhals_lazyframe__()
+            self._compliant_frame: Any = df.__narwhals_lazyframe__()
         elif is_polars and (
             (pl := get_polars()) is not None and isinstance(df, pl.LazyFrame)
         ):
-            self._dataframe = df
+            self._compliant_frame = df
         else:
             msg = f"Expected Polars LazyFrame or object that implements `__narwhals_lazyframe__`, got: {type(df)}"
             raise TypeError(msg)
@@ -1894,7 +1896,7 @@ class LazyFrame(BaseFrame[FrameT]):
             └─────┴─────┴─────┘
         """
         return DataFrame(
-            self._dataframe.collect(),
+            self._compliant_frame.collect(),
             is_polars=self._is_polars,
             backend_version=self._backend_version,
         )
diff --git a/narwhals/expression.py b/narwhals/expression.py
index e265a085..95346dbf 100644
--- a/narwhals/expression.py
+++ b/narwhals/expression.py
@@ -25,7 +25,7 @@ def extract_native(expr: Expr, other: Any) -> Any:
     if isinstance(other, Expr):
         return other._call(expr)
     if isinstance(other, Series):
-        return other._series
+        return other._compliant_series
     return other
 
 
diff --git a/narwhals/functions.py b/narwhals/functions.py
index 36f03a7f..172fe3b6 100644
--- a/narwhals/functions.py
+++ b/narwhals/functions.py
@@ -34,8 +34,8 @@ def concat(
     validate_laziness(items)
     first_item = items[0]
     plx = first_item.__narwhals_namespace__()
-    return first_item._from_dataframe(  # type: ignore[return-value]
-        plx.concat([df._dataframe for df in items], how=how),
+    return first_item._from_compliant_dataframe(  # type: ignore[return-value]
+        plx.concat([df._compliant_frame for df in items], how=how),
     )
 
 
diff --git a/narwhals/group_by.py b/narwhals/group_by.py
index b0d83b40..ed5f6281 100644
--- a/narwhals/group_by.py
+++ b/narwhals/group_by.py
@@ -24,7 +24,7 @@ class GroupBy(Generic[DataFrameT]):
     def __init__(self, df: DataFrameT, *keys: str | Iterable[str]) -> None:
         self._df = cast(DataFrame[Any], df)
         self._keys = flatten(keys)
-        self._grouped = self._df._dataframe.group_by(self._keys)
+        self._grouped = self._df._compliant_frame.group_by(self._keys)
 
     def agg(
         self, *aggs: IntoExpr | Iterable[IntoExpr], **named_aggs: IntoExpr
@@ -110,13 +110,13 @@ class GroupBy(Generic[DataFrameT]):
             └─────┴─────┴─────┘
         """
         aggs, named_aggs = self._df._flatten_and_extract(*aggs, **named_aggs)
-        return self._df._from_dataframe(  # type: ignore[return-value]
+        return self._df._from_compliant_dataframe(  # type: ignore[return-value]
             self._grouped.agg(*aggs, **named_aggs),
         )
 
     def __iter__(self) -> Iterator[tuple[Any, DataFrameT]]:
         yield from (  # type: ignore[misc]
-            (tupleify(key), self._df._from_dataframe(df))
+            (tupleify(key), self._df._from_compliant_dataframe(df))
             for (key, df) in self._grouped.__iter__()
         )
 
@@ -125,12 +125,12 @@ class LazyGroupBy(Generic[LazyFrameT]):
     def __init__(self, df: LazyFrameT, *keys: str | Iterable[str]) -> None:
         self._df = cast(LazyFrame[Any], df)
         self._keys = keys
-        self._grouped = self._df._dataframe.group_by(*self._keys)
+        self._grouped = self._df._compliant_frame.group_by(*self._keys)
 
     def agg(
         self, *aggs: IntoExpr | Iterable[IntoExpr], **named_aggs: IntoExpr
     ) -> LazyFrameT:
         aggs, named_aggs = self._df._flatten_and_extract(*aggs, **named_aggs)
-        return self._df._from_dataframe(  # type: ignore[return-value]
+        return self._df._from_compliant_dataframe(  # type: ignore[return-value]
             self._grouped.agg(*aggs, **named_aggs),
         )
diff --git a/narwhals/series.py b/narwhals/series.py
index ade685f3..b0cd69d2 100644
--- a/narwhals/series.py
+++ b/narwhals/series.py
@@ -36,11 +36,11 @@ class Series:
         self._is_polars = is_polars
         self._backend_version = backend_version
         if hasattr(series, "__narwhals_series__"):
-            self._series = series.__narwhals_series__()
+            self._compliant_series = series.__narwhals_series__()
         elif is_polars and (
             (pl := get_polars()) is not None and isinstance(series, pl.Series)
         ):
-            self._series = series
+            self._compliant_series = series
         else:
             msg = (  # pragma: no cover
                 f"Expected Polars Series or and object which implements `__narwhals_series__`, got: {type(series)}."
@@ -49,23 +49,23 @@ class Series:
 
     def __array__(self, dtype: Any = None, copy: bool | None = None) -> np.ndarray:
         if self._is_polars and self._backend_version < (0, 20, 29):  # pragma: no cover
-            return self._series.__array__(dtype=dtype)
-        return self._series.__array__(dtype=dtype, copy=copy)
+            return self._compliant_series.__array__(dtype=dtype)
+        return self._compliant_series.__array__(dtype=dtype, copy=copy)
 
     def __getitem__(self, idx: int | slice) -> Any:
         if isinstance(idx, int):
-            return self._series[idx]
-        return self._from_series(self._series[idx])
+            return self._compliant_series[idx]
+        return self._from_compliant_series(self._compliant_series[idx])
 
     def __native_namespace__(self) -> Any:
         if self._is_polars:
             return get_polars()
-        return self._series.__native_namespace__()
+        return self._compliant_series.__native_namespace__()
 
     def __narwhals_namespace__(self) -> Any:
         if self._is_polars:
             return get_polars()
-        return self._series.__narwhals_namespace__()
+        return self._compliant_series.__narwhals_namespace__()
 
     @property
     def shape(self) -> tuple[int]:
@@ -93,16 +93,16 @@ class Series:
             >>> func(s_pl)
             (3,)
         """
-        return self._series.shape  # type: ignore[no-any-return]
+        return self._compliant_series.shape  # type: ignore[no-any-return]
 
     def _extract_native(self, arg: Any) -> Any:
         from narwhals.series import Series
 
         if isinstance(arg, Series):
-            return arg._series
+            return arg._compliant_series
         return arg
 
-    def _from_series(self, series: Any) -> Self:
+    def _from_compliant_series(self, series: Any) -> Self:
         return self.__class__(
             series, is_polars=self._is_polars, backend_version=self._backend_version
         )
@@ -122,7 +122,7 @@ class Series:
         )
 
     def __len__(self) -> int:
-        return len(self._series)
+        return len(self._compliant_series)
 
     def len(self) -> int:
         r"""
@@ -151,7 +151,7 @@ class Series:
             >>> func(s_pl)  # doctest: +NORMALIZE_WHITESPACE
             3
         """
-        return len(self._series)
+        return len(self._compliant_series)
 
     @property
     def dtype(self) -> Any:
@@ -179,7 +179,7 @@ class Series:
             >>> func(s_pl)
             Int64
         """
-        return to_narwhals_dtype(self._series.dtype, is_polars=self._is_polars)
+        return to_narwhals_dtype(self._compliant_series.dtype, is_polars=self._is_polars)
 
     @property
     def name(self) -> str:
@@ -207,7 +207,7 @@ class Series:
             >>> func(s_pl)
             'foo'
         """
-        return self._series.name  # type: ignore[no-any-return]
+        return self._compliant_series.name  # type: ignore[no-any-return]
 
     def cast(
         self,
@@ -249,8 +249,10 @@ class Series:
                1
             ]
         """
-        return self._from_series(
-            self._series.cast(translate_dtype(self.__narwhals_namespace__(), dtype))
+        return self._from_compliant_series(
+            self._compliant_series.cast(
+                translate_dtype(self.__narwhals_namespace__(), dtype)
+            )
         )
 
     def to_frame(self) -> DataFrame[Any]:
@@ -293,7 +295,7 @@ class Series:
         from narwhals.dataframe import DataFrame
 
         return DataFrame(
-            self._series.to_frame(),
+            self._compliant_series.to_frame(),
             is_polars=self._is_polars,
             backend_version=self._backend_version,
         )
@@ -323,7 +325,7 @@ class Series:
             >>> func(s_pl)
             [1, 2, 3]
         """
-        return self._series.to_list()  # type: ignore[no-any-return]
+        return self._compliant_series.to_list()  # type: ignore[no-any-return]
 
     def mean(self) -> Any:
         """
@@ -350,7 +352,7 @@ class Series:
             >>> func(s_pl)
             2.0
         """
-        return self._series.mean()
+        return self._compliant_series.mean()
 
     def any(self) -> Any:
         """
@@ -380,7 +382,7 @@ class Series:
             >>> func(s_pl)
             True
         """
-        return self._series.any()
+        return self._compliant_series.any()
 
     def all(self) -> Any:
         """
@@ -408,7 +410,7 @@ class Series:
             False
 
         """
-        return self._series.all()
+        return self._compliant_series.all()
 
     def min(self) -> Any:
         """
@@ -435,7 +437,7 @@ class Series:
             >>> func(s_pl)
             1
         """
-        return self._series.min()
+        return self._compliant_series.min()
 
     def max(self) -> Any:
         """
@@ -462,7 +464,7 @@ class Series:
             >>> func(s_pl)
             3
         """
-        return self._series.max()
+        return self._compliant_series.max()
 
     def sum(self) -> Any:
         """
@@ -489,7 +491,7 @@ class Series:
             >>> func(s_pl)
             6
         """
-        return self._series.sum()
+        return self._compliant_series.sum()
 
     def std(self, *, ddof: int = 1) -> Any:
         """
@@ -520,7 +522,7 @@ class Series:
             >>> func(s_pl)
             1.0
         """
-        return self._series.std(ddof=ddof)
+        return self._compliant_series.std(ddof=ddof)
 
     def is_in(self, other: Any) -> Self:
         """
@@ -558,7 +560,9 @@ class Series:
                true
             ]
         """
-        return self._from_series(self._series.is_in(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.is_in(self._extract_native(other))
+        )
 
     def drop_nulls(self) -> Self:
         """
@@ -603,7 +607,7 @@ class Series:
              5
           ]
         """
-        return self._from_series(self._series.drop_nulls())
+        return self._from_compliant_series(self._compliant_series.drop_nulls())
 
     def abs(self) -> Self:
         """
@@ -639,7 +643,7 @@ class Series:
                3
             ]
         """
-        return self._from_series(self._series.abs())
+        return self._from_compliant_series(self._compliant_series.abs())
 
     def cum_sum(self) -> Self:
         """
@@ -675,7 +679,7 @@ class Series:
                9
             ]
         """
-        return self._from_series(self._series.cum_sum())
+        return self._from_compliant_series(self._compliant_series.cum_sum())
 
     def unique(self) -> Self:
         """
@@ -711,7 +715,7 @@ class Series:
                6
             ]
         """
-        return self._from_series(self._series.unique())
+        return self._from_compliant_series(self._compliant_series.unique())
 
     def diff(self) -> Self:
         """
@@ -756,7 +760,7 @@ class Series:
                -1
             ]
         """
-        return self._from_series(self._series.diff())
+        return self._from_compliant_series(self._compliant_series.diff())
 
     def shift(self, n: int) -> Self:
         """
@@ -805,7 +809,7 @@ class Series:
                4
             ]
         """
-        return self._from_series(self._series.shift(n))
+        return self._from_compliant_series(self._compliant_series.shift(n))
 
     def sample(
         self,
@@ -861,8 +865,10 @@ class Series:
                4
             ]
         """
-        return self._from_series(
-            self._series.sample(n=n, fraction=fraction, with_replacement=with_replacement)
+        return self._from_compliant_series(
+            self._compliant_series.sample(
+                n=n, fraction=fraction, with_replacement=with_replacement
+            )
         )
 
     def alias(self, name: str) -> Self:
@@ -902,7 +908,7 @@ class Series:
                3
             ]
         """
-        return self._from_series(self._series.alias(name=name))
+        return self._from_compliant_series(self._compliant_series.alias(name=name))
 
     def sort(self, *, descending: bool = False) -> Self:
         """
@@ -962,7 +968,9 @@ class Series:
                1
             ]
         """
-        return self._from_series(self._series.sort(descending=descending))
+        return self._from_compliant_series(
+            self._compliant_series.sort(descending=descending)
+        )
 
     def is_null(self) -> Self:
         """
@@ -1002,7 +1010,7 @@ class Series:
                true
             ]
         """
-        return self._from_series(self._series.is_null())
+        return self._from_compliant_series(self._compliant_series.is_null())
 
     def fill_null(self, value: Any) -> Self:
         """
@@ -1045,7 +1053,7 @@ class Series:
                5
             ]
         """
-        return self._from_series(self._series.fill_null(value))
+        return self._from_compliant_series(self._compliant_series.fill_null(value))
 
     def is_between(
         self, lower_bound: Any, upper_bound: Any, closed: str = "both"
@@ -1097,8 +1105,8 @@ class Series:
                false
             ]
         """
-        return self._from_series(
-            self._series.is_between(lower_bound, upper_bound, closed=closed)
+        return self._from_compliant_series(
+            self._compliant_series.is_between(lower_bound, upper_bound, closed=closed)
         )
 
     def n_unique(self) -> int:
@@ -1126,7 +1134,7 @@ class Series:
             >>> func(s_pl)
             3
         """
-        return self._series.n_unique()  # type: ignore[no-any-return]
+        return self._compliant_series.n_unique()  # type: ignore[no-any-return]
 
     def to_numpy(self) -> Any:
         """
@@ -1153,7 +1161,7 @@ class Series:
             >>> func(s_pl)
             array([1, 2, 3]...)
         """
-        return self._series.to_numpy()
+        return self._compliant_series.to_numpy()
 
     def to_pandas(self) -> Any:
         """
@@ -1186,74 +1194,116 @@ class Series:
             2    3
             Name: a, dtype: int64
         """
-        return self._series.to_pandas()
+        return self._compliant_series.to_pandas()
 
     def __add__(self, other: object) -> Self:
-        return self._from_series(self._series.__add__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__add__(self._extract_native(other))
+        )
 
     def __radd__(self, other: object) -> Self:
-        return self._from_series(self._series.__radd__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__radd__(self._extract_native(other))
+        )
 
     def __sub__(self, other: object) -> Self:
-        return self._from_series(self._series.__sub__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__sub__(self._extract_native(other))
+        )
 
     def __rsub__(self, other: object) -> Self:
-        return self._from_series(self._series.__rsub__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__rsub__(self._extract_native(other))
+        )
 
     def __mul__(self, other: object) -> Self:
-        return self._from_series(self._series.__mul__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__mul__(self._extract_native(other))
+        )
 
     def __rmul__(self, other: object) -> Self:
-        return self._from_series(self._series.__rmul__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__rmul__(self._extract_native(other))
+        )
 
     def __truediv__(self, other: object) -> Self:
-        return self._from_series(self._series.__truediv__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__truediv__(self._extract_native(other))
+        )
 
     def __floordiv__(self, other: object) -> Self:
-        return self._from_series(self._series.__floordiv__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__floordiv__(self._extract_native(other))
+        )
 
     def __rfloordiv__(self, other: object) -> Self:
-        return self._from_series(self._series.__rfloordiv__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__rfloordiv__(self._extract_native(other))
+        )
 
     def __pow__(self, other: object) -> Self:
-        return self._from_series(self._series.__pow__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__pow__(self._extract_native(other))
+        )
 
     def __rpow__(self, other: object) -> Self:
-        return self._from_series(self._series.__rpow__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__rpow__(self._extract_native(other))
+        )
 
     def __mod__(self, other: object) -> Self:
-        return self._from_series(self._series.__mod__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__mod__(self._extract_native(other))
+        )
 
     def __rmod__(self, other: object) -> Self:
-        return self._from_series(self._series.__rmod__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__rmod__(self._extract_native(other))
+        )
 
     def __eq__(self, other: object) -> Self:  # type: ignore[override]
-        return self._from_series(self._series.__eq__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__eq__(self._extract_native(other))
+        )
 
     def __ne__(self, other: object) -> Self:  # type: ignore[override]
-        return self._from_series(self._series.__ne__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__ne__(self._extract_native(other))
+        )
 
     def __gt__(self, other: Any) -> Self:
-        return self._from_series(self._series.__gt__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__gt__(self._extract_native(other))
+        )
 
     def __ge__(self, other: Any) -> Self:  # pragma: no cover (todo)
-        return self._from_series(self._series.__ge__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__ge__(self._extract_native(other))
+        )
 
     def __lt__(self, other: Any) -> Self:  # pragma: no cover (todo)
-        return self._from_series(self._series.__lt__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__lt__(self._extract_native(other))
+        )
 
     def __le__(self, other: Any) -> Self:  # pragma: no cover (todo)
-        return self._from_series(self._series.__le__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__le__(self._extract_native(other))
+        )
 
     def __and__(self, other: Any) -> Self:  # pragma: no cover (todo)
-        return self._from_series(self._series.__and__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__and__(self._extract_native(other))
+        )
 
     def __or__(self, other: Any) -> Self:  # pragma: no cover (todo)
-        return self._from_series(self._series.__or__(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.__or__(self._extract_native(other))
+        )
 
     # unary
     def __invert__(self) -> Self:
-        return self._from_series(self._series.__invert__())
+        return self._from_compliant_series(self._compliant_series.__invert__())
 
     def filter(self, other: Any) -> Self:
         """
@@ -1289,7 +1339,9 @@ class Series:
                50
             ]
         """
-        return self._from_series(self._series.filter(self._extract_native(other)))
+        return self._from_compliant_series(
+            self._compliant_series.filter(self._extract_native(other))
+        )
 
     # --- descriptive ---
     def is_duplicated(self: Self) -> Self:
@@ -1327,7 +1379,7 @@ class Series:
                 true
             ]
         """
-        return self._from_series(self._series.is_duplicated())
+        return self._from_compliant_series(self._compliant_series.is_duplicated())
 
     def is_empty(self: Self) -> bool:
         r"""
@@ -1357,7 +1409,7 @@ class Series:
             >>> func(s_pd), func(s_pl)
             (False, False)
         """
-        return self._series.is_empty()  # type: ignore[no-any-return]
+        return self._compliant_series.is_empty()  # type: ignore[no-any-return]
 
     def is_unique(self: Self) -> Self:
         r"""
@@ -1395,7 +1447,7 @@ class Series:
                 false
             ]
         """
-        return self._from_series(self._series.is_unique())
+        return self._from_compliant_series(self._compliant_series.is_unique())
 
     def null_count(self: Self) -> int:
         r"""
@@ -1426,7 +1478,7 @@ class Series:
             2
         """
 
-        return self._series.null_count()  # type: ignore[no-any-return]
+        return self._compliant_series.null_count()  # type: ignore[no-any-return]
 
     def is_first_distinct(self: Self) -> Self:
         r"""
@@ -1466,7 +1518,7 @@ class Series:
                 false
             ]
         """
-        return self._from_series(self._series.is_first_distinct())
+        return self._from_compliant_series(self._compliant_series.is_first_distinct())
 
     def is_last_distinct(self: Self) -> Self:
         r"""
@@ -1506,7 +1558,7 @@ class Series:
                 true
             ]
         """
-        return self._from_series(self._series.is_last_distinct())
+        return self._from_compliant_series(self._compliant_series.is_last_distinct())
 
     def is_sorted(self: Self, *, descending: bool = False) -> bool:
         r"""
@@ -1539,7 +1591,7 @@ class Series:
             >>> func(pd.Series(sorted_data), descending=True)
             True
         """
-        return self._series.is_sorted(descending=descending)  # type: ignore[no-any-return]
+        return self._compliant_series.is_sorted(descending=descending)  # type: ignore[no-any-return]
 
     def value_counts(
         self: Self, *, sort: bool = False, parallel: bool = False
@@ -1588,7 +1640,7 @@ class Series:
         from narwhals.dataframe import DataFrame
 
         return DataFrame(
-            self._series.value_counts(sort=sort, parallel=parallel),
+            self._compliant_series.value_counts(sort=sort, parallel=parallel),
             is_polars=self._is_polars,
             backend_version=self._backend_version,
         )
@@ -1635,7 +1687,9 @@ class Series:
             >>> func(s_pl)  # doctest: +NORMALIZE_WHITESPACE
             [5.0, 12.0, 25.0, 37.0, 44.0]
         """
-        return self._series.quantile(quantile=quantile, interpolation=interpolation)
+        return self._compliant_series.quantile(
+            quantile=quantile, interpolation=interpolation
+        )
 
     def zip_with(self, mask: Any, other: Any) -> Self:
         """
@@ -1679,8 +1733,10 @@ class Series:
             dtype: int64
         """
 
-        return self._from_series(
-            self._series.zip_with(self._extract_native(mask), self._extract_native(other))
+        return self._from_compliant_series(
+            self._compliant_series.zip_with(
+                self._extract_native(mask), self._extract_native(other)
+            )
         )
 
     def item(self: Self, index: int | None = None) -> Any:
@@ -1709,7 +1765,7 @@ class Series:
             >>> func(pl.Series("a", [9, 8, 7]), -1), func(pl.Series([9, 8, 7]), -2)
             (7, 8)
         """
-        return self._series.item(index=index)
+        return self._compliant_series.item(index=index)
 
     def head(self: Self, n: int = 10) -> Self:
         r"""
@@ -1751,7 +1807,7 @@ class Series:
             ]
         """
 
-        return self._from_series(self._series.head(n))
+        return self._from_compliant_series(self._compliant_series.head(n))
 
     def tail(self: Self, n: int = 10) -> Self:
         r"""
@@ -1792,7 +1848,7 @@ class Series:
             ]
         """
 
-        return self._from_series(self._series.tail(n))
+        return self._from_compliant_series(self._compliant_series.tail(n))
 
     def round(self: Self, decimals: int = 0) -> Self:
         r"""
@@ -1840,7 +1896,7 @@ class Series:
                3.9
             ]
         """
-        return self._from_series(self._series.round(decimals))
+        return self._from_compliant_series(self._compliant_series.round(decimals))
 
     @property
     def str(self) -> SeriesStringNamespace:
@@ -1857,7 +1913,7 @@ class Series:
 
 class SeriesCatNamespace:
     def __init__(self, series: Series) -> None:
-        self._series = series
+        self._narwhals_series = series
 
     def get_categories(self) -> Series:
         """
@@ -1894,12 +1950,14 @@ class SeriesCatNamespace:
                "mango"
             ]
         """
-        return self._series._from_series(self._series._series.cat.get_categories())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.cat.get_categories()
+        )
 
 
 class SeriesStringNamespace:
     def __init__(self, series: Series) -> None:
-        self._series = series
+        self._narwhals_series = series
 
     def starts_with(self, prefix: str) -> Series:
         r"""
@@ -1939,7 +1997,9 @@ class SeriesStringNamespace:
                null
             ]
         """
-        return self._series._from_series(self._series._series.str.starts_with(prefix))
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.starts_with(prefix)
+        )
 
     def ends_with(self, suffix: str) -> Series:
         r"""
@@ -1979,7 +2039,9 @@ class SeriesStringNamespace:
                null
             ]
         """
-        return self._series._from_series(self._series._series.str.ends_with(suffix))
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.ends_with(suffix)
+        )
 
     def contains(self, pattern: str, *, literal: bool = False) -> Series:
         r"""
@@ -2025,8 +2087,8 @@ class SeriesStringNamespace:
                null
             ]
         """
-        return self._series._from_series(
-            self._series._series.str.contains(pattern, literal=literal)
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.contains(pattern, literal=literal)
         )
 
     def slice(self, offset: int, length: int | None = None) -> Series:
@@ -2094,8 +2156,10 @@ class SeriesStringNamespace:
                 "uit"
             ]
         """
-        return self._series._from_series(
-            self._series._series.str.slice(offset=offset, length=length)
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.slice(
+                offset=offset, length=length
+            )
         )
 
     def head(self, n: int = 5) -> Series:
@@ -2142,7 +2206,9 @@ class SeriesStringNamespace:
                "zukky"
             ]
         """
-        return self._series._from_series(self._series._series.str.slice(0, n))
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.slice(0, n)
+        )
 
     def tail(self, n: int = 5) -> Series:
         r"""
@@ -2188,7 +2254,9 @@ class SeriesStringNamespace:
                "kkyun"
             ]
         """
-        return self._series._from_series(self._series._series.str.slice(-n))
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.slice(-n)
+        )
 
     def to_uppercase(self) -> Series:
         r"""
@@ -2234,7 +2302,9 @@ class SeriesStringNamespace:
             └────────┴───────────┘
 
         """
-        return self._series._from_series(self._series._series.str.to_uppercase())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.to_uppercase()
+        )
 
     def to_lowercase(self) -> Series:
         r"""
@@ -2275,12 +2345,14 @@ class SeriesStringNamespace:
             │ null   ┆ null      │
             └────────┴───────────┘
         """
-        return self._series._from_series(self._series._series.str.to_lowercase())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.str.to_lowercase()
+        )
 
 
 class SeriesDateTimeNamespace:
     def __init__(self, series: Series) -> None:
-        self._series = series
+        self._narwhals_series = series
 
     def year(self) -> Series:
         """
@@ -2315,7 +2387,9 @@ class SeriesDateTimeNamespace:
                2023
             ]
         """
-        return self._series._from_series(self._series._series.dt.year())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.year()
+        )
 
     def month(self) -> Series:
         """
@@ -2350,7 +2424,9 @@ class SeriesDateTimeNamespace:
                8
             ]
         """
-        return self._series._from_series(self._series._series.dt.month())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.month()
+        )
 
     def day(self) -> Series:
         """
@@ -2385,7 +2461,9 @@ class SeriesDateTimeNamespace:
                5
             ]
         """
-        return self._series._from_series(self._series._series.dt.day())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.day()
+        )
 
     def hour(self) -> Series:
         """
@@ -2420,7 +2498,9 @@ class SeriesDateTimeNamespace:
                9
             ]
         """
-        return self._series._from_series(self._series._series.dt.hour())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.hour()
+        )
 
     def minute(self) -> Series:
         """
@@ -2455,7 +2535,9 @@ class SeriesDateTimeNamespace:
                12
             ]
         """
-        return self._series._from_series(self._series._series.dt.minute())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.minute()
+        )
 
     def second(self) -> Series:
         """
@@ -2490,7 +2572,9 @@ class SeriesDateTimeNamespace:
                 4
             ]
         """
-        return self._series._from_series(self._series._series.dt.second())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.second()
+        )
 
     def millisecond(self) -> Series:
         """
@@ -2538,7 +2622,9 @@ class SeriesDateTimeNamespace:
                 200
             ]
         """
-        return self._series._from_series(self._series._series.dt.millisecond())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.millisecond()
+        )
 
     def microsecond(self) -> Series:
         """
@@ -2586,7 +2672,9 @@ class SeriesDateTimeNamespace:
                200000
             ]
         """
-        return self._series._from_series(self._series._series.dt.microsecond())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.microsecond()
+        )
 
     def nanosecond(self) -> Series:
         """
@@ -2624,7 +2712,9 @@ class SeriesDateTimeNamespace:
                60000000
             ]
         """
-        return self._series._from_series(self._series._series.dt.nanosecond())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.nanosecond()
+        )
 
     def ordinal_day(self) -> Series:
         """
@@ -2659,7 +2749,9 @@ class SeriesDateTimeNamespace:
                216
             ]
         """
-        return self._series._from_series(self._series._series.dt.ordinal_day())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.ordinal_day()
+        )
 
     def total_minutes(self) -> Series:
         """
@@ -2699,7 +2791,9 @@ class SeriesDateTimeNamespace:
                     20
             ]
         """
-        return self._series._from_series(self._series._series.dt.total_minutes())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.total_minutes()
+        )
 
     def total_seconds(self) -> Series:
         """
@@ -2739,7 +2833,9 @@ class SeriesDateTimeNamespace:
                     20
             ]
         """
-        return self._series._from_series(self._series._series.dt.total_seconds())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.total_seconds()
+        )
 
     def total_milliseconds(self) -> Series:
         """
@@ -2782,7 +2878,9 @@ class SeriesDateTimeNamespace:
                     20
             ]
         """
-        return self._series._from_series(self._series._series.dt.total_milliseconds())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.total_milliseconds()
+        )
 
     def total_microseconds(self) -> Series:
         """
@@ -2825,7 +2923,9 @@ class SeriesDateTimeNamespace:
                     1200
             ]
         """
-        return self._series._from_series(self._series._series.dt.total_microseconds())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.total_microseconds()
+        )
 
     def total_nanoseconds(self) -> Series:
         """
@@ -2865,7 +2965,9 @@ class SeriesDateTimeNamespace:
                     1
             ]
         """
-        return self._series._from_series(self._series._series.dt.total_nanoseconds())
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.total_nanoseconds()
+        )
 
     def to_string(self, format: str) -> Series:  # noqa: A002
         """
@@ -2938,4 +3040,6 @@ class SeriesDateTimeNamespace:
                "2020/05/01"
             ]
         """
-        return self._series._from_series(self._series._series.dt.to_string(format))
+        return self._narwhals_series._from_compliant_series(
+            self._narwhals_series._compliant_series.dt.to_string(format)
+        )
diff --git a/narwhals/stable/v1.py b/narwhals/stable/v1.py
index f1194b3e..86602a1d 100644
--- a/narwhals/stable/v1.py
+++ b/narwhals/stable/v1.py
@@ -429,15 +429,21 @@ def _stableify(
 ) -> DataFrame[IntoFrameT] | LazyFrame[IntoFrameT] | Series | Expr | Any:
     if isinstance(obj, NwDataFrame):
         return DataFrame(
-            obj._dataframe, is_polars=obj._is_polars, backend_version=obj._backend_version
+            obj._compliant_frame,
+            is_polars=obj._is_polars,
+            backend_version=obj._backend_version,
         )
     if isinstance(obj, NwLazyFrame):
         return LazyFrame(
-            obj._dataframe, is_polars=obj._is_polars, backend_version=obj._backend_version
+            obj._compliant_frame,
+            is_polars=obj._is_polars,
+            backend_version=obj._backend_version,
         )
     if isinstance(obj, NwSeries):
         return Series(
-            obj._series, is_polars=obj._is_polars, backend_version=obj._backend_version
+            obj._compliant_series,
+            is_polars=obj._is_polars,
+            backend_version=obj._backend_version,
         )
     if isinstance(obj, NwExpr):
         return Expr(obj._call)
diff --git a/narwhals/translate.py b/narwhals/translate.py
index 8e7ee6cd..04051d9d 100644
--- a/narwhals/translate.py
+++ b/narwhals/translate.py
@@ -58,15 +58,15 @@ def to_native(
 
     if isinstance(narwhals_object, BaseFrame):
         return (
-            narwhals_object._dataframe
+            narwhals_object._compliant_frame
             if narwhals_object._is_polars
-            else narwhals_object._dataframe._native_dataframe
+            else narwhals_object._compliant_frame._native_dataframe
         )
     if isinstance(narwhals_object, Series):
         return (
-            narwhals_object._series
+            narwhals_object._compliant_series
             if narwhals_object._is_polars
-            else narwhals_object._series._native_series
+            else narwhals_object._compliant_series._native_series
         )
 
     if strict:  # pragma: no cover (todo)
diff --git a/narwhals/utils.py b/narwhals/utils.py
index a974493c..83e2b384 100644
--- a/narwhals/utils.py
+++ b/narwhals/utils.py
@@ -88,8 +88,8 @@ def isinstance_or_issubclass(obj: Any, cls: Any) -> bool:
 def validate_same_library(items: Iterable[Any]) -> None:
     if all(item._is_polars for item in items):
         return
-    if all(hasattr(item._dataframe, "_implementation") for item in items) and (
-        len({item._dataframe._implementation for item in items}) == 1
+    if all(hasattr(item._compliant_frame, "_implementation") for item in items) and (
+        len({item._compliant_frame._implementation for item in items}) == 1
     ):
         return
     raise NotImplementedError("Cross-library comparisons aren't supported")
@@ -142,50 +142,52 @@ def maybe_align_index(lhs: T, rhs: Series | BaseFrame[Any]) -> T:
 
     lhs_any = cast(Any, lhs)
     rhs_any = cast(Any, rhs)
-    if isinstance(getattr(lhs_any, "_dataframe", None), PandasDataFrame) and isinstance(
-        getattr(rhs_any, "_dataframe", None), PandasDataFrame
-    ):
-        _validate_index(lhs_any._dataframe._native_dataframe.index)
-        _validate_index(rhs_any._dataframe._native_dataframe.index)
-        return lhs_any._from_dataframe(  # type: ignore[no-any-return]
-            lhs_any._dataframe._from_native_dataframe(
-                lhs_any._dataframe._native_dataframe.loc[
-                    rhs_any._dataframe._native_dataframe.index
+    if isinstance(
+        getattr(lhs_any, "_compliant_frame", None), PandasDataFrame
+    ) and isinstance(getattr(rhs_any, "_compliant_frame", None), PandasDataFrame):
+        _validate_index(lhs_any._compliant_frame._native_dataframe.index)
+        _validate_index(rhs_any._compliant_frame._native_dataframe.index)
+        return lhs_any._from_compliant_dataframe(  # type: ignore[no-any-return]
+            lhs_any._compliant_frame._from_native_dataframe(
+                lhs_any._compliant_frame._native_dataframe.loc[
+                    rhs_any._compliant_frame._native_dataframe.index
                 ]
             )
         )
-    if isinstance(getattr(lhs_any, "_dataframe", None), PandasDataFrame) and isinstance(
-        getattr(rhs_any, "_series", None), PandasSeries
-    ):
-        _validate_index(lhs_any._dataframe._native_dataframe.index)
-        _validate_index(rhs_any._series._native_series.index)
-        return lhs_any._from_dataframe(  # type: ignore[no-any-return]
-            lhs_any._dataframe._from_native_dataframe(
-                lhs_any._dataframe._native_dataframe.loc[
-                    rhs_any._series._native_series.index
+    if isinstance(
+        getattr(lhs_any, "_compliant_frame", None), PandasDataFrame
+    ) and isinstance(getattr(rhs_any, "_compliant_series", None), PandasSeries):
+        _validate_index(lhs_any._compliant_frame._native_dataframe.index)
+        _validate_index(rhs_any._compliant_series._native_series.index)
+        return lhs_any._from_compliant_dataframe(  # type: ignore[no-any-return]
+            lhs_any._compliant_frame._from_native_dataframe(
+                lhs_any._compliant_frame._native_dataframe.loc[
+                    rhs_any._compliant_series._native_series.index
                 ]
             )
         )
-    if isinstance(getattr(lhs_any, "_series", None), PandasSeries) and isinstance(
-        getattr(rhs_any, "_dataframe", None), PandasDataFrame
-    ):
-        _validate_index(lhs_any._series._native_series.index)
-        _validate_index(rhs_any._dataframe._native_dataframe.index)
-        return lhs_any._from_series(  # type: ignore[no-any-return]
-            lhs_any._series._from_native_series(
-                lhs_any._series._native_series.loc[
-                    rhs_any._dataframe._native_dataframe.index
+    if isinstance(
+        getattr(lhs_any, "_compliant_series", None), PandasSeries
+    ) and isinstance(getattr(rhs_any, "_compliant_frame", None), PandasDataFrame):
+        _validate_index(lhs_any._compliant_series._native_series.index)
+        _validate_index(rhs_any._compliant_frame._native_dataframe.index)
+        return lhs_any._from_compliant_series(  # type: ignore[no-any-return]
+            lhs_any._compliant_series._from_native_series(
+                lhs_any._compliant_series._native_series.loc[
+                    rhs_any._compliant_frame._native_dataframe.index
                 ]
             )
         )
-    if isinstance(getattr(lhs_any, "_series", None), PandasSeries) and isinstance(
-        getattr(rhs_any, "_series", None), PandasSeries
-    ):
-        _validate_index(lhs_any._series._native_series.index)
-        _validate_index(rhs_any._series._native_series.index)
-        return lhs_any._from_series(  # type: ignore[no-any-return]
-            lhs_any._series._from_native_series(
-                lhs_any._series._native_series.loc[rhs_any._series._native_series.index]
+    if isinstance(
+        getattr(lhs_any, "_compliant_series", None), PandasSeries
+    ) and isinstance(getattr(rhs_any, "_compliant_series", None), PandasSeries):
+        _validate_index(lhs_any._compliant_series._native_series.index)
+        _validate_index(rhs_any._compliant_series._native_series.index)
+        return lhs_any._from_compliant_series(  # type: ignore[no-any-return]
+            lhs_any._compliant_series._from_native_series(
+                lhs_any._compliant_series._native_series.loc[
+                    rhs_any._compliant_series._native_series.index
+                ]
             )
         )
     if len(lhs_any) != len(rhs_any):
@@ -220,10 +222,10 @@ def maybe_set_index(df: T, column_names: str | list[str]) -> T:
     from narwhals._pandas_like.dataframe import PandasDataFrame
 
     df_any = cast(Any, df)
-    if isinstance(getattr(df_any, "_dataframe", None), PandasDataFrame):
-        return df_any._from_dataframe(  # type: ignore[no-any-return]
-            df_any._dataframe._from_native_dataframe(
-                df_any._dataframe._native_dataframe.set_index(column_names)
+    if isinstance(getattr(df_any, "_compliant_frame", None), PandasDataFrame):
+        return df_any._from_compliant_dataframe(  # type: ignore[no-any-return]
+            df_any._compliant_frame._from_native_dataframe(
+                df_any._compliant_frame._native_dataframe.set_index(column_names)
             )
         )
     return df
@@ -257,10 +259,10 @@ def maybe_convert_dtypes(df: T, *args: bool, **kwargs: bool | str) -> T:
     from narwhals._pandas_like.dataframe import PandasDataFrame
 
     df_any = cast(Any, df)
-    if isinstance(getattr(df_any, "_dataframe", None), PandasDataFrame):
-        return df_any._from_dataframe(  # type: ignore[no-any-return]
-            df_any._dataframe._from_native_dataframe(
-                df_any._dataframe._native_dataframe.convert_dtypes(*args, **kwargs)
+    if isinstance(getattr(df_any, "_compliant_frame", None), PandasDataFrame):
+        return df_any._from_compliant_dataframe(  # type: ignore[no-any-return]
+            df_any._compliant_frame._from_native_dataframe(
+                df_any._compliant_frame._native_dataframe.convert_dtypes(*args, **kwargs)
             )
         )
     return df


Test patch:
diff --git a/tests/frame/clone_test.py b/tests/frame/clone_test.py
index 5d157c60..017ed018 100644
--- a/tests/frame/clone_test.py
+++ b/tests/frame/clone_test.py
@@ -9,5 +9,5 @@ def test_clone(constructor_with_lazy: Any) -> None:
     df = nw.from_native(constructor_with_lazy(expected))
     df_clone = df.clone()
     assert df is not df_clone
-    assert df._dataframe is not df_clone._dataframe
+    assert df._compliant_frame is not df_clone._compliant_frame
     compare_dicts(df_clone, expected)
diff --git a/tests/frame/join_test.py b/tests/frame/join_test.py
index 61ded0de..a8339277 100644
--- a/tests/frame/join_test.py
+++ b/tests/frame/join_test.py
@@ -48,7 +48,7 @@ def test_cross_join_non_pandas() -> None:
     data = {"a": [1, 3, 2]}
     df = nw.from_native(pd.DataFrame(data))
     # HACK to force testing for a non-pandas codepath
-    df._dataframe._implementation = Implementation.MODIN
+    df._compliant_frame._implementation = Implementation.MODIN
     result = df.join(df, how="cross")  # type: ignore[arg-type]
     expected = {"a": [1, 1, 1, 3, 3, 3, 2, 2, 2], "a_right": [1, 3, 2, 1, 3, 2, 1, 3, 2]}
     compare_dicts(result, expected)