flags.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. from __future__ import annotations
  2. from typing import TYPE_CHECKING
  3. import weakref
  4. if TYPE_CHECKING:
  5. from pandas.core.generic import NDFrame
  6. class Flags:
  7. """
  8. Flags that apply to pandas objects.
  9. Parameters
  10. ----------
  11. obj : Series or DataFrame
  12. The object these flags are associated with.
  13. allows_duplicate_labels : bool, default True
  14. Whether to allow duplicate labels in this object. By default,
  15. duplicate labels are permitted. Setting this to ``False`` will
  16. cause an :class:`errors.DuplicateLabelError` to be raised when
  17. `index` (or columns for DataFrame) is not unique, or any
  18. subsequent operation on introduces duplicates.
  19. See :ref:`duplicates.disallow` for more.
  20. .. warning::
  21. This is an experimental feature. Currently, many methods fail to
  22. propagate the ``allows_duplicate_labels`` value. In future versions
  23. it is expected that every method taking or returning one or more
  24. DataFrame or Series objects will propagate ``allows_duplicate_labels``.
  25. Examples
  26. --------
  27. Attributes can be set in two ways:
  28. >>> df = pd.DataFrame()
  29. >>> df.flags
  30. <Flags(allows_duplicate_labels=True)>
  31. >>> df.flags.allows_duplicate_labels = False
  32. >>> df.flags
  33. <Flags(allows_duplicate_labels=False)>
  34. >>> df.flags['allows_duplicate_labels'] = True
  35. >>> df.flags
  36. <Flags(allows_duplicate_labels=True)>
  37. """
  38. _keys: set[str] = {"allows_duplicate_labels"}
  39. def __init__(self, obj: NDFrame, *, allows_duplicate_labels: bool) -> None:
  40. self._allows_duplicate_labels = allows_duplicate_labels
  41. self._obj = weakref.ref(obj)
  42. @property
  43. def allows_duplicate_labels(self) -> bool:
  44. """
  45. Whether this object allows duplicate labels.
  46. Setting ``allows_duplicate_labels=False`` ensures that the
  47. index (and columns of a DataFrame) are unique. Most methods
  48. that accept and return a Series or DataFrame will propagate
  49. the value of ``allows_duplicate_labels``.
  50. See :ref:`duplicates` for more.
  51. See Also
  52. --------
  53. DataFrame.attrs : Set global metadata on this object.
  54. DataFrame.set_flags : Set global flags on this object.
  55. Examples
  56. --------
  57. >>> df = pd.DataFrame({"A": [1, 2]}, index=['a', 'a'])
  58. >>> df.flags.allows_duplicate_labels
  59. True
  60. >>> df.flags.allows_duplicate_labels = False
  61. Traceback (most recent call last):
  62. ...
  63. pandas.errors.DuplicateLabelError: Index has duplicates.
  64. positions
  65. label
  66. a [0, 1]
  67. """
  68. return self._allows_duplicate_labels
  69. @allows_duplicate_labels.setter
  70. def allows_duplicate_labels(self, value: bool) -> None:
  71. value = bool(value)
  72. obj = self._obj()
  73. if obj is None:
  74. raise ValueError("This flag's object has been deleted.")
  75. if not value:
  76. for ax in obj.axes:
  77. ax._maybe_check_unique()
  78. self._allows_duplicate_labels = value
  79. def __getitem__(self, key: str):
  80. if key not in self._keys:
  81. raise KeyError(key)
  82. return getattr(self, key)
  83. def __setitem__(self, key: str, value) -> None:
  84. if key not in self._keys:
  85. raise ValueError(f"Unknown flag {key}. Must be one of {self._keys}")
  86. setattr(self, key, value)
  87. def __repr__(self) -> str:
  88. return f"<Flags(allows_duplicate_labels={self.allows_duplicate_labels})>"
  89. def __eq__(self, other) -> bool:
  90. if isinstance(other, type(self)):
  91. return self.allows_duplicate_labels == other.allows_duplicate_labels
  92. return False