diff --git a/src/safeds/data/image/containers/_image.py b/src/safeds/data/image/containers/_image.py index 4f4f73045..f3ff799f6 100644 --- a/src/safeds/data/image/containers/_image.py +++ b/src/safeds/data/image/containers/_image.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import io import warnings from pathlib import Path @@ -108,6 +109,16 @@ def __eq__(self, other: object) -> bool: and torch.all(torch.eq(self._image_tensor, other._set_device(self.device)._image_tensor)).item() ) + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return sys.getsizeof(self._image_tensor) + self._image_tensor.element_size() * self._image_tensor.nelement() + def _repr_jpeg_(self) -> bytes | None: """ Return a JPEG image as bytes. diff --git a/src/safeds/data/tabular/containers/_column.py b/src/safeds/data/tabular/containers/_column.py index 07742c86e..e776d13c9 100644 --- a/src/safeds/data/tabular/containers/_column.py +++ b/src/safeds/data/tabular/containers/_column.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import io from collections.abc import Sequence from numbers import Number @@ -247,6 +248,16 @@ def __repr__(self) -> str: """ return f"Column({self._name!r}, {list(self._data)!r})" + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return sys.getsizeof(self._data) + sys.getsizeof(self._name) + sys.getsizeof(self._type) + def __str__(self) -> str: """ Return a user-friendly string representation of this column. diff --git a/src/safeds/data/tabular/containers/_row.py b/src/safeds/data/tabular/containers/_row.py index b4d4c53ca..1c91a1592 100644 --- a/src/safeds/data/tabular/containers/_row.py +++ b/src/safeds/data/tabular/containers/_row.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import functools from collections.abc import Callable, Mapping from typing import TYPE_CHECKING, Any @@ -269,6 +270,16 @@ def __repr__(self) -> str: """ return f"Row({self!s})" + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return sys.getsizeof(self._data) + sys.getsizeof(self._schema) + def __str__(self) -> str: """ Return a user-friendly string representation of this row. diff --git a/src/safeds/data/tabular/containers/_table.py b/src/safeds/data/tabular/containers/_table.py index a5a855a79..a189e034d 100644 --- a/src/safeds/data/tabular/containers/_table.py +++ b/src/safeds/data/tabular/containers/_table.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys import functools import io import warnings @@ -475,6 +476,16 @@ def __repr__(self) -> str: tmp.columns = self.column_names return tmp.__repr__() + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return sys.getsizeof(self._data) + sys.getsizeof(self._schema) + def __str__(self) -> str: tmp = self._data.reset_index(drop=True) tmp.columns = self.column_names diff --git a/src/safeds/data/tabular/containers/_tagged_table.py b/src/safeds/data/tabular/containers/_tagged_table.py index e0d092aef..f50124718 100644 --- a/src/safeds/data/tabular/containers/_tagged_table.py +++ b/src/safeds/data/tabular/containers/_tagged_table.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from typing import TYPE_CHECKING from safeds.data.tabular.containers import Column, Row, Table @@ -164,6 +165,16 @@ def __init__( self._features: Table = _data.keep_only_columns(feature_names) self._target: Column = _data.get_column(target_name) + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return Table.__sizeof__(self) + sys.getsizeof(self._features) + sys.getsizeof(self._target) + # ------------------------------------------------------------------------------------------------------------------ # Properties # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/containers/_time_series.py b/src/safeds/data/tabular/containers/_time_series.py index 7f18b5475..491b7d68b 100644 --- a/src/safeds/data/tabular/containers/_time_series.py +++ b/src/safeds/data/tabular/containers/_time_series.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from typing import TYPE_CHECKING from safeds.data.tabular.containers import Column, Row, Table, TaggedTable @@ -186,6 +187,16 @@ def __init__( raise UnknownColumnNameError([time_name]) self._time: Column = _data.get_column(time_name) + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return TaggedTable.__sizeof__(self) + sys.getsizeof(self._time) + # ------------------------------------------------------------------------------------------------------------------ # Properties # ------------------------------------------------------------------------------------------------------------------ diff --git a/src/safeds/data/tabular/typing/_schema.py b/src/safeds/data/tabular/typing/_schema.py index 32a6bc3af..56ee3c453 100644 --- a/src/safeds/data/tabular/typing/_schema.py +++ b/src/safeds/data/tabular/typing/_schema.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from dataclasses import dataclass from typing import TYPE_CHECKING @@ -100,6 +101,16 @@ def __repr__(self) -> str: """ return f"Schema({self!s})" + def __sizeof__(self) -> int: + """ + Return the complete size of this object. + + Returns + ------- + Size of this object in bytes. + """ + return sum(map(sys.getsizeof, self._schema.keys())) + sum(map(sys.getsizeof, self._schema.values())) + sys.getsizeof(self._schema) + def __str__(self) -> str: """ Return a user-friendly string representation of the schema. diff --git a/tests/safeds/data/image/containers/test_image.py b/tests/safeds/data/image/containers/test_image.py index 0b09e0dd2..4c7897010 100644 --- a/tests/safeds/data/image/containers/test_image.py +++ b/tests/safeds/data/image/containers/test_image.py @@ -1,3 +1,4 @@ +import sys import typing from pathlib import Path from tempfile import NamedTemporaryFile @@ -900,3 +901,16 @@ def test_should_return_edges_of_image( image_edges = image.find_edges() assert image_edges == snapshot_png _assert_width_height_channel(image, image_edges) + + +@pytest.mark.parametrize("device", _test_devices(), ids=_test_devices_ids()) +class TestSizeof: + @pytest.mark.parametrize( + "resource_path", + _test_images_all(), + ids=_test_images_all_ids(), + ) + def test_should_size_be_greater_than_normal_object(self, resource_path: str | Path, device: Device) -> None: + _skip_if_device_not_available(device) + image = Image.from_file(resolve_resource_path(resource_path), device) + assert sys.getsizeof(image) >= image.width * image.height * image.channel diff --git a/tests/safeds/data/tabular/containers/_column/test_sizeof.py b/tests/safeds/data/tabular/containers/_column/test_sizeof.py new file mode 100644 index 000000000..fb3de3dc4 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_column/test_sizeof.py @@ -0,0 +1,21 @@ +import sys + +import pytest +from safeds.data.tabular.containers import Column + + +@pytest.mark.parametrize( + "column", + [ + Column("a", []), + Column("a", [0]), + Column("a", [0, "1"]), + ], + ids=[ + "empty", + "one row", + "multiple rows", + ], +) +def test_should_size_be_greater_than_normal_object(column: Column) -> None: + assert sys.getsizeof(column) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py b/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py new file mode 100644 index 000000000..fbe310894 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_table/_tagged_table/_time_series/test_sizeof.py @@ -0,0 +1,39 @@ +import sys + +import pytest +from safeds.data.tabular.containers import Table, TimeSeries + + +@pytest.mark.parametrize( + "time_series", + [ + + TimeSeries( + { + "time": [0, 1, 2], + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "target": [1, 3, 2], + }, + "target", + "time", + ["feature_1", "feature_2"], + ), + + TimeSeries( + { + "time": [0, 1, 2], + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "other": [3, 9, 12], + "target": [1, 3, 2], + }, + "target", + "time", + ["feature_1", "feature_2"], + ), + ], + ids=["normal", "table_with_column_as_non_feature"], +) +def test_should_size_be_greater_than_normal_object(time_series: TimeSeries) -> None: + assert sys.getsizeof(time_series) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_table/_tagged_table/test_sizeof.py b/tests/safeds/data/tabular/containers/_table/_tagged_table/test_sizeof.py new file mode 100644 index 000000000..d6013c502 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_table/_tagged_table/test_sizeof.py @@ -0,0 +1,35 @@ +import sys + +import pytest +from safeds.data.tabular.containers import Table, TaggedTable + + +@pytest.mark.parametrize( + "tagged_table", + [ + + TaggedTable( + { + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "target": [1, 3, 2], + }, + "target", + ["feature_1", "feature_2"], + ), + + TaggedTable( + { + "feature_1": [3, 9, 6], + "feature_2": [6, 12, 9], + "other": [3, 9, 12], + "target": [1, 3, 2], + }, + "target", + ["feature_1", "feature_2"], + ), + ], + ids=["normal", "table_with_column_as_non_feature"], +) +def test_should_size_be_greater_than_normal_object(tagged_table: TaggedTable) -> None: + assert sys.getsizeof(tagged_table) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/_table/test_sizeof.py b/tests/safeds/data/tabular/containers/_table/test_sizeof.py new file mode 100644 index 000000000..69d896aa9 --- /dev/null +++ b/tests/safeds/data/tabular/containers/_table/test_sizeof.py @@ -0,0 +1,21 @@ +import sys + +import pytest +from safeds.data.tabular.containers import Table + + +@pytest.mark.parametrize( + "table", + [ + Table(), + Table({"col1": [0]}), + Table({"col1": [0, "1"], "col2": ["a", "b"]}), + ], + ids=[ + "empty table", + "table with one row", + "table with multiple rows", + ], +) +def test_should_size_be_greater_than_normal_object(table: Table) -> None: + assert sys.getsizeof(table) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/containers/test_row.py b/tests/safeds/data/tabular/containers/test_row.py index 9f4e6d774..61ee4a04d 100644 --- a/tests/safeds/data/tabular/containers/test_row.py +++ b/tests/safeds/data/tabular/containers/test_row.py @@ -1,4 +1,5 @@ import re +import sys from collections.abc import Callable from typing import Any @@ -552,3 +553,21 @@ def test_should_sort_columns(self, row: Row, comparator: Callable[[tuple, tuple] def test_should_sort_table_out_of_place(self, row: Row) -> None: sorted_row = row.sort_columns() assert sorted_row != row + + +class TestSizeof: + @pytest.mark.parametrize( + "row", + [ + Row(), + Row({"col1": 0}), + Row({"col1": 0, "col2": "a"}), + ], + ids=[ + "empty", + "single column", + "multiple columns", + ], + ) + def test_should_size_be_greater_than_normal_object(self, row: Row) -> None: + assert sys.getsizeof(row) > sys.getsizeof(object()) diff --git a/tests/safeds/data/tabular/typing/test_schema.py b/tests/safeds/data/tabular/typing/test_schema.py index f6cd256d5..f2a8cebbe 100644 --- a/tests/safeds/data/tabular/typing/test_schema.py +++ b/tests/safeds/data/tabular/typing/test_schema.py @@ -1,5 +1,6 @@ from __future__ import annotations +import sys from typing import TYPE_CHECKING import pandas as pd @@ -495,3 +496,21 @@ class TestReprMarkdown: ) def test_should_create_a_string_representation(self, schema: Schema, expected: str) -> None: assert schema._repr_markdown_() == expected + + +class TestSizeof: + @pytest.mark.parametrize( + "schema", + [ + Schema({}), + Schema({"A": Integer()}), + Schema({"A": Integer(), "B": String()}), + ], + ids=[ + "empty", + "single column", + "multiple columns", + ], + ) + def test_should_size_be_greater_than_normal_object(self, schema: Schema) -> None: + assert sys.getsizeof(schema) > sys.getsizeof(object())