On Mon, Oct 14, 2019 at 8:48 AM Steve Jorgensen
It is usually quite meaningful to drill down into other kinds of immutable collections. A data structure consisting of a hierarchy of tuples would be a good example of that.
Besides the drill-down, cases, there are times when it is valuable to test whether a collection or a "scalar" was received as an argument. That is to be avoided more often than not, but there are times (such as when implementing a DSL where it is useful enough to want to do.
def __setattr__(self, value_or_vl): # When value_or_vl is a non-string sequence, then it is VERY likely # to be a tuple (which is immutable). if isinstance(Sequence, value_or_vl) and not isinstance(Scalar, value_or_vl): value, label = value_or_vl else: value = value_or_vl label = None
self.values.add(value) self.value_labels['value'] = label or self.value_labels.get(value)
If the code above were to specifically check for `str`, `bytes`, or `bytearray`, that would be more fragile. it would not accommodate any new stringlike types in newer python versions, and there would be no way to designate other custom classes as being logically scalar even though supporting sequence behavior.
I'm not really sure that there's a general problem to be solved here. You want to drill down into all containers except strings, so why not just say that? Scalar = (str, bytes) If a future Python version introduces a new string-like type, you would have to make a decision as to whether it should be treated as a string or as a collection of strings. (Assuming it isn't actually a subclass of str or bytes, in which case it's been made clear.) Whether something should be treated as atomic or iterable depends on usage, not just the inherent attributes of the type; for instance, a namedtuple is iterable (as a subclass of tuple), but many namedtuple instances should be treated as atomic, and not drilled down into. (Classic example: a point, defined as (x,y), should almost certainly be seen as atomic. Even more so if you define a point as (R, theta).) Alternatively, do what str.startswith/str.endswith do and mandate that a pair be one of a specific set of types. That's usually the easiest plan when working with both sequences and mappings; you can define sequence behaviour as applying ONLY to tuples and lists (and maybe sets), mapping behaviour applying ONLY to dicts, and every other object will be treated as a scalar. It's a small restriction on your caller, and a massive simplicity. But if you truly want to support arbitrary sequences (or perhaps arbitrary containers, or arbitrary iterables), then just special-case whichever string types make sense to you, and run with it. ChrisA