Skip to content

Commit c16fcc4

Browse files
h-vetinaritm9k1
authored andcommitted
Add allow_sets-kwarg to is_list_like (pandas-dev#23065)
1 parent f490328 commit c16fcc4

File tree

5 files changed

+85
-26
lines changed

5 files changed

+85
-26
lines changed

doc/source/whatsnew/v0.24.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ Other Enhancements
198198
- :meth:`round`, :meth:`ceil`, and meth:`floor` for :class:`DatetimeIndex` and :class:`Timestamp` now support an ``ambiguous`` argument for handling datetimes that are rounded to ambiguous times (:issue:`18946`)
199199
- :class:`Resampler` now is iterable like :class:`GroupBy` (:issue:`15314`).
200200
- :meth:`Series.resample` and :meth:`DataFrame.resample` have gained the :meth:`Resampler.quantile` (:issue:`15023`).
201+
- :meth:`pandas.core.dtypes.is_list_like` has gained a keyword ``allow_sets`` which is ``True`` by default; if ``False``,
202+
all instances of ``set`` will not be considered "list-like" anymore (:issue:`23061`)
201203
- :meth:`Index.to_frame` now supports overriding column name(s) (:issue:`22580`).
202204
- New attribute :attr:`__git_version__` will return git commit sha of current build (:issue:`21295`).
203205
- Compatibility with Matplotlib 3.0 (:issue:`22790`).

pandas/compat/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ def lfilter(*args, **kwargs):
141141
Mapping = collections.abc.Mapping
142142
Sequence = collections.abc.Sequence
143143
Sized = collections.abc.Sized
144+
Set = collections.abc.Set
144145

145146
else:
146147
# Python 2
@@ -201,6 +202,7 @@ def get_range_parameters(data):
201202
Mapping = collections.Mapping
202203
Sequence = collections.Sequence
203204
Sized = collections.Sized
205+
Set = collections.Set
204206

205207
if PY2:
206208
def iteritems(obj, **kw):

pandas/core/dtypes/common.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
ABCSparseArray, ABCSparseSeries, ABCCategoricalIndex, ABCIndexClass,
1717
ABCDateOffset)
1818
from pandas.core.dtypes.inference import ( # noqa:F401
19-
is_bool, is_integer, is_hashable, is_iterator, is_float,
20-
is_dict_like, is_scalar, is_string_like, is_list_like, is_number,
21-
is_file_like, is_re, is_re_compilable, is_sequence, is_nested_list_like,
22-
is_named_tuple, is_array_like, is_decimal, is_complex, is_interval)
19+
is_bool, is_integer, is_float, is_number, is_decimal, is_complex,
20+
is_re, is_re_compilable, is_dict_like, is_string_like, is_file_like,
21+
is_list_like, is_nested_list_like, is_sequence, is_named_tuple,
22+
is_hashable, is_iterator, is_array_like, is_scalar, is_interval)
2323

2424
_POSSIBLY_CAST_DTYPES = {np.dtype(t).name
2525
for t in ['O', 'int8', 'uint8', 'int16', 'uint16',

pandas/core/dtypes/inference.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from numbers import Number
66
from pandas import compat
77
from pandas.compat import (PY2, string_types, text_type,
8-
string_and_binary_types, re_type)
8+
string_and_binary_types, re_type, Set)
99
from pandas._libs import lib
1010

1111
is_bool = lib.is_bool
@@ -247,7 +247,7 @@ def is_re_compilable(obj):
247247
return True
248248

249249

250-
def is_list_like(obj):
250+
def is_list_like(obj, allow_sets=True):
251251
"""
252252
Check if the object is list-like.
253253
@@ -259,6 +259,10 @@ def is_list_like(obj):
259259
Parameters
260260
----------
261261
obj : The object to check.
262+
allow_sets : boolean, default True
263+
If this parameter is False, sets will not be considered list-like
264+
265+
.. versionadded:: 0.24.0
262266
263267
Returns
264268
-------
@@ -283,11 +287,15 @@ def is_list_like(obj):
283287
False
284288
"""
285289

286-
return (isinstance(obj, compat.Iterable) and
290+
return (isinstance(obj, compat.Iterable)
287291
# we do not count strings/unicode/bytes as list-like
288-
not isinstance(obj, string_and_binary_types) and
292+
and not isinstance(obj, string_and_binary_types)
293+
289294
# exclude zero-dimensional numpy arrays, effectively scalars
290-
not (isinstance(obj, np.ndarray) and obj.ndim == 0))
295+
and not (isinstance(obj, np.ndarray) and obj.ndim == 0)
296+
297+
# exclude sets if allow_sets is False
298+
and not (allow_sets is False and isinstance(obj, Set)))
291299

292300

293301
def is_array_like(obj):

pandas/tests/dtypes/test_inference.py

+64-17
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,70 @@ def coerce(request):
4848
return request.param
4949

5050

51+
# collect all objects to be tested for list-like-ness; use tuples of objects,
52+
# whether they are list-like or not (special casing for sets), and their ID
53+
ll_params = [
54+
([1], True, 'list'), # noqa: E241
55+
([], True, 'list-empty'), # noqa: E241
56+
((1, ), True, 'tuple'), # noqa: E241
57+
(tuple(), True, 'tuple-empty'), # noqa: E241
58+
({'a': 1}, True, 'dict'), # noqa: E241
59+
(dict(), True, 'dict-empty'), # noqa: E241
60+
({'a', 1}, 'set', 'set'), # noqa: E241
61+
(set(), 'set', 'set-empty'), # noqa: E241
62+
(frozenset({'a', 1}), 'set', 'frozenset'), # noqa: E241
63+
(frozenset([]), 'set', 'frozenset-empty'), # noqa: E241
64+
(iter([1, 2]), True, 'iterator'), # noqa: E241
65+
(iter([]), True, 'iterator-empty'), # noqa: E241
66+
((x for x in [1, 2]), True, 'generator'), # noqa: E241
67+
((x for x in []), True, 'generator-empty'), # noqa: E241
68+
(Series([1]), True, 'Series'), # noqa: E241
69+
(Series([]), True, 'Series-empty'), # noqa: E241
70+
(Series(['a']).str, True, 'StringMethods'), # noqa: E241
71+
(Series([], dtype='O').str, True, 'StringMethods-empty'), # noqa: E241
72+
(Index([1]), True, 'Index'), # noqa: E241
73+
(Index([]), True, 'Index-empty'), # noqa: E241
74+
(DataFrame([[1]]), True, 'DataFrame'), # noqa: E241
75+
(DataFrame(), True, 'DataFrame-empty'), # noqa: E241
76+
(np.ndarray((2,) * 1), True, 'ndarray-1d'), # noqa: E241
77+
(np.array([]), True, 'ndarray-1d-empty'), # noqa: E241
78+
(np.ndarray((2,) * 2), True, 'ndarray-2d'), # noqa: E241
79+
(np.array([[]]), True, 'ndarray-2d-empty'), # noqa: E241
80+
(np.ndarray((2,) * 3), True, 'ndarray-3d'), # noqa: E241
81+
(np.array([[[]]]), True, 'ndarray-3d-empty'), # noqa: E241
82+
(np.ndarray((2,) * 4), True, 'ndarray-4d'), # noqa: E241
83+
(np.array([[[[]]]]), True, 'ndarray-4d-empty'), # noqa: E241
84+
(np.array(2), False, 'ndarray-0d'), # noqa: E241
85+
(1, False, 'int'), # noqa: E241
86+
(b'123', False, 'bytes'), # noqa: E241
87+
(b'', False, 'bytes-empty'), # noqa: E241
88+
('123', False, 'string'), # noqa: E241
89+
('', False, 'string-empty'), # noqa: E241
90+
(str, False, 'string-type'), # noqa: E241
91+
(object(), False, 'object'), # noqa: E241
92+
(np.nan, False, 'NaN'), # noqa: E241
93+
(None, False, 'None') # noqa: E241
94+
]
95+
objs, expected, ids = zip(*ll_params)
96+
97+
98+
@pytest.fixture(params=zip(objs, expected), ids=ids)
99+
def maybe_list_like(request):
100+
return request.param
101+
102+
103+
def test_is_list_like(maybe_list_like):
104+
obj, expected = maybe_list_like
105+
expected = True if expected == 'set' else expected
106+
assert inference.is_list_like(obj) == expected
107+
108+
109+
def test_is_list_like_disallow_sets(maybe_list_like):
110+
obj, expected = maybe_list_like
111+
expected = False if expected == 'set' else expected
112+
assert inference.is_list_like(obj, allow_sets=False) == expected
113+
114+
51115
def test_is_sequence():
52116
is_seq = inference.is_sequence
53117
assert (is_seq((1, 2)))
@@ -64,23 +128,6 @@ def __getitem__(self):
64128
assert (not is_seq(A()))
65129

66130

67-
@pytest.mark.parametrize(
68-
"ll",
69-
[
70-
[], [1], (1, ), (1, 2), {'a': 1},
71-
{1, 'a'}, Series([1]),
72-
Series([]), Series(['a']).str,
73-
np.array([2])])
74-
def test_is_list_like_passes(ll):
75-
assert inference.is_list_like(ll)
76-
77-
78-
@pytest.mark.parametrize(
79-
"ll", [1, '2', object(), str, np.array(2)])
80-
def test_is_list_like_fails(ll):
81-
assert not inference.is_list_like(ll)
82-
83-
84131
def test_is_array_like():
85132
assert inference.is_array_like(Series([]))
86133
assert inference.is_array_like(Series([1, 2]))

0 commit comments

Comments
 (0)