set operations on sets with more than one of the same member

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

38 Replies - 2060 Views - Last Post: 13 March 2018 - 01:54 PM Rate Topic: -----

#31 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 11 March 2018 - 12:07 PM

View Postjon.kiparsky, on 11 March 2018 - 07:09 PM, said:

What do you think? Do you think you got where you wanted to with it? Did you enjoy writing it? Did you enjoy having a few tests on hand to drive your work? :)
If you were going to improve anything here, where would you go next?

Oh, I enjoyed it very much. And I learned much.
It is the first time I've written functions like __sub__, __and__, etc.
The programmed testing was also new to me.

Even when writing my last post, I did realize that I should swap the inner representation from list to dict. So this is still work in progress. Something I definitely intend to do in the next days (some business travel may delay it a few days).

I have not opened your two spoilers, - and will not right now. I'll fight my way a little longer.

For info, I'll place my current version; it is improved on the __eq__ function (turning toward use of dictionary), and also on the testing part, making the individual test functions obsolete. Also I have included true sets in the test, ensuring that this Multiset class handles set correctly:
Spoiler

Was This Post Helpful? 1
  • +
  • -

#32 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 11 March 2018 - 12:15 PM

View Postjon.kiparsky, on 11 March 2018 - 07:09 PM, said:

Quote

I'm not so happy with the __eq__ function


Since you called that out in particular, yeah, that feels a little forced. One simple improvement would be to notice that you have an underlying representation which knows about equality. If sorted(self.lst) == sorted(other.lst) then that should do you, right? :)/>

I tried this first, but this road is blocked when the list holds both numbers and strings; sorted() cannot compare int and str. As shown in my post above, I solved it using dictionaries.
Was This Post Helpful? 1
  • +
  • -

#33 jon.kiparsky   User is online

  • Beginner
  • member icon


Reputation: 11134
  • View blog
  • Posts: 19,077
  • Joined: 19-March 11

Re: set operations on sets with more than one of the same member

Posted 11 March 2018 - 12:29 PM

Good point. I think your solution is the correct one. (I didn't want to presume you were willing to switch your underlying representation, but clearly that's a winning move)
Was This Post Helpful? 0
  • +
  • -

#34 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 12 March 2018 - 03:53 PM

So, after a long day I finally got time to this.
I've modified the code, completely abandoning the self._lst and now only rely on self._frq
I find the new __sub__ and __or__ functions quite 'ugly' but cannot right now find a better way. They both work so maybe I should suffice. My stomach feeling, however, is that it is not clean and should be improved. I'll look at this tomorrow.

I've never used dictionaries much, so maybe I'm missing something basic that could clean up the code...?

I'm deliberately not using the Counter method from collections. This is because I want to maximize the learning, not the program performance. If this should be used for something important, I would use the more optimized functions in the collections library.

The current version, and since no-one else play along, without spoiler protection:
class Multiset():
    """
    Multiset instances are describtions of frequency of occurance in a list.
    
    A number of operations are possible using Multiset instances, actually the
    same operations and operators as for normal set:

    * a - c, returns data in 'a' but not in 'c'
    * a | c, returns data in a or c or both
    * a & c, returns data in both 'a' and 'c'
    * a ^ c, returns data in 'a' or 'c' but not both

    * to_list(), returns an ordinary list
    * freq(), returns data as ordinary dictionary
    * to_set(), returns data as set

    Made by DK3250
    March 12, 2018
    
    Test functions inspired by jon.kiparsky
    """
    def __init__(self, lst):
        def _frequence(_lst):
            self._frq = {}
            for item in _lst:
                if item in self._frq.keys():
                    self._frq[item] += 1
                else:
                    self._frq[item] = 1
        
        try:
            _lst = list(lst)
        except TypeError:
            print("Illegal input: '{0}'".format(lst))
            print("Argument cannot be converted to list")
            return

        _frequence(_lst)

    def __repr__(self):
        if hasattr(self, '_frq'):
            return "Multiset({0})".format(self.freq())
        else:
            return "Instance does not exist"

    def __sub__(self, other):
        " items in self but not in other "
        result = Multiset([])
        
        for key, value in self._frq.items():
            if key in other._frq.keys():
                v = value - other._frq[key]
                if v > 0:  # avoiding negative or zero frequencies
                    result._frq[key] = v
            else:
                result._frq[key] = value

        return result

    def __or__(self, other):
        "items in self or other or both"
        result = Multiset([])

        for key, value in self._frq.items():
            if key in other._frq.keys():
                result._frq[key] = value + other._frq[key]
            else:
                result._frq[key] = value
                
        for key, value in other._frq.items():
            if key not in self._frq.keys():
                result._frq[key] = value

        return result

    def __and__(self, other):
        "items in both self and other"
        result = Multiset([])

        for key, value in self._frq.items():
            if key in other._frq.keys():
                result._frq[key] = min(value, other._frq[key])

        return result

    def __xor__(self, other):
        "items in self or other but not both"
        a = self | other
        b = self & other
        return a - b - b

    def __eq__(self, other):
        "returns 'True' if data are equal, else 'False'"
        return self._frq == other._frq

    def to_list(self):
        result = []
        for key, value in self._frq.items():
            for _ in range(value):
                result.append(key)
        return result

    def to_set(self):
        return set(self.to_list())

    def freq(self):
        return self._frq


def run_tests():
    failures = []
    for i, test_case in enumerate(all_test, 1):
        try:
            assert test_case
            print (".", end="")  # visual representation of success.
        except Assertionerror:
            failures.append(i)
            print ("F", end="") 
##        except:  # intentional bare except
##            pass
    print("")
    print_test_report(failures) 

def print_test_report(failures):
    if failures:
        print ("You had %d failing tests. Cases %s failed." % (len(failures), ", ".join(map(str, failures))))
    

## TEST
m1 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five"])
m2 = Multiset([1,1,2,"six"])
m3 = Multiset([2,3,3,3,4,4,4,"five", "five"])  # sub
m4 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five",1,1,2,"six"])  # or
m5 = Multiset([1,2])  # and
m6 = Multiset([1,2,3,3,3,4,4,4,"five", "five", "six"])  # xor

j1 = Multiset([1,1,2,3,4,4])
j2 = Multiset([1,2,2,4,4])
j3 = Multiset([1,2,4,4])
j4 = Multiset([1,2,3])

a = set('abracadabra')
b = set('alacazam')


all_test = [m1 and m2 == m2 and m1,
            m1 - m2 == m3,
            m1 | m2 == m4,
            m1 & m2 == m5,
            m1 ^ m2 == m6,
            j1 & j2 == j3,
            j1 ^ j2 == j4,
            a - b == {'r', 'd', 'b'},
            a | b == {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'},
            a & b == {'a', 'c'},
            a ^ b == {'r', 'd', 'b', 'm', 'z', 'l'},
            Multiset(m1.to_list()) == Multiset([1,2,2,3,3,3,4,4,4,"five", "five"]),
            ]

run_tests()


Was This Post Helpful? 1
  • +
  • -

#35 jon.kiparsky   User is online

  • Beginner
  • member icon


Reputation: 11134
  • View blog
  • Posts: 19,077
  • Joined: 19-March 11

Re: set operations on sets with more than one of the same member

Posted 12 March 2018 - 05:39 PM

Hm. A few stray thoughts, in no particular order:

- For your __sub__ function you might consider using max to select either the calculated value or zero.

- I would think that zero frequency would be allowable, for example for the following

>>> ms = Multiset([1])
>>> ms - ms



I would expect an empty Multiset as the result.
You might want to have a "normalizing" function that deletes zero-frequency entries. Something like
self._frq = {key:val for key, val in self._frq.items() if val > 0}

This would be a useful way to ensure that you don't have to constantly remember that bit of logic.

- the .get() function will be useful to you. Example: if f = {1:2, 3:4}.get(5, 0), f ends up having the value 0. This is a good way to avoid if-checks on dictionary lookups.

- I might be tempted to express that __sub__ calculation as a dict comp, but my first attempt showed me that that required a little bit of thought, so I'm putting that off for later.

- in your first test notice that there's a distinction between and and & in python. One is the logical and operator, and the other runs your __and__ function. Similarly for or and |. The logical operators both return the last item evaluated. Considering the effects of short-circuit evaluation, this can be a nice way of expressing some alternatives:

>>> 1 and 2
2
>>> 1 & 2
0
>>> [1] and [2]
[2]
>>> [1] or [2]
[1]
>>> [] and [2]
[]
>>> [] or [2]
[2]
>>> def some_func(foo=None):
...   return foo or 7
...
>>> some_func(2)
2
>>> some_func()
7



Also, == and and have different precedence - the comparison operators are actually higher than the boolean operators. So your first test is not expressed quite the way you want it, I think.

What's actually happening in that test is illustrated by this little example:
>>> def print_and_return(x):
    def f():
        print("f%d" %x)
        return x
    return f

>>> def make_fs(n):
    return [print_and_return(x) for x in range(n)]

>>> f0, f1, f2, f3, f4 = make_fs(5)
>>> value = f1() and f2() == f2() and f1()
f1
f2
f2
f1
>>> value
1
>>>



Not sure if this example is too cryptic. If it is I can unpack it.

Might have some more thoughts later.
Was This Post Helpful? 1
  • +
  • -

#36 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 12 March 2018 - 07:31 PM

I was already testing the max() function and dict comprehension; and had the idea of a normalize function. Could not get it to work.

The dict.get() did the trick. Much cleaner code now.

So, it was something basic I was missing - didn't know about this function. SIC.

I'll look into your comments about test and boolean vs. bitwise operators tomorrow.
The use of and in test 1, however, is downright a simple error on my side - I didn't see it because the test result was ok.

For the record, my current code (it is late, no time to polish, so pretty raw):
class Multiset():
    """
    Multiset instances are describtions of frequency of occurance in a list.
    
    A number of operations are possible using Multiset instances, actually the
    same operations and operators as for normal set:

    * a - c, returns data in 'a' but not in 'c'
    * a | c, returns data in a or c or both
    * a & c, returns data in both 'a' and 'c'
    * a ^ c, returns data in 'a' or 'c' but not both

    * to_list(), returns an ordinary list
    * freq(), returns data as ordinary dictionary
    * to_set(), returns data as set

    Made by DK3250
    March 12, 2018
    
    Test functions inspired by jon.kiparsky
    """
    def __init__(self, lst):
        def _frequence(_lst):
            self._frq = {}
            for item in _lst:
                if item in self._frq.keys():
                    self._frq[item] += 1
                else:
                    self._frq[item] = 1
        
        try:
            _lst = list(lst)
        except TypeError:
            print("Illegal input: '{0}'".format(lst))
            print("Argument cannot be converted to list")
            return

        _frequence(_lst)

    def __repr__(self):
        if hasattr(self, '_frq'):
            return "Multiset({0})".format(self.freq())
        else:
            return "Instance does not exist"

    def __sub__(self, other):
        " items in self but not in other "
        result = Multiset([])
        
        for key, value in self._frq.items():
            result._frq[key] = max(0, value - other._frq.get(key, 0))

        result._normalize()
        return result

    def __or__(self, other):
        "items in self or other or both"
        result = Multiset([])

        for key, value in self._frq.items():
            result._frq[key] = value + other._frq.get(key, 0)
                
        for key, value in other._frq.items():
            if key not in self._frq.keys():
                result._frq[key] = value

        return result

    def __and__(self, other):
        "items in both self and other"
        result = Multiset([])

        for key, value in self._frq.items():
            if key in other._frq.keys():
                result._frq[key] = min(value, other._frq[key])

        return result

    def __xor__(self, other):
        "items in self or other but not both"
        a = self | other
        b = self & other
        return a - b - b

    def __eq__(self, other):
        "returns 'True' if data are equal, else 'False'"
        return self._frq == other._frq

    def _normalize(self):
        self._frq = {key:val for key, val in self._frq.items() if val > 0}

    def to_list(self):
        result = []
        for key, value in self._frq.items():
            for _ in range(value):
                result.append(key)
        return result

    def to_set(self):
        return set(self.to_list())

    def freq(self):
        return self._frq


def run_tests():
    failures = []
    for i, test_case in enumerate(all_test, 1):
        try:
            assert test_case
            print (".", end="")  # visual representation of success.
        except Assertionerror:
            failures.append(i)
            print ("F", end="") 
##        except:  # intentional bare except
##            pass
    print("")
    print_test_report(failures) 

def print_test_report(failures):
    if failures:
        print ("You had %d failing tests. Cases %s failed." % (len(failures), ", ".join(map(str, failures))))
    

## TEST
m1 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five"])
m2 = Multiset([1,1,2,"six"])
m3 = Multiset([2,3,3,3,4,4,4,"five", "five"])  # sub
m4 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five",1,1,2,"six"])  # or
m5 = Multiset([1,2])  # and
m6 = Multiset([1,2,3,3,3,4,4,4,"five", "five", "six"])  # xor

j1 = Multiset([1,1,2,3,4,4])
j2 = Multiset([1,2,2,4,4])
j3 = Multiset([1,2,4,4])
j4 = Multiset([1,2,3])

a = set('abracadabra')
b = set('alacazam')


all_test = [m1 & m2 == m2 & m1,
            m1 - m2 == m3,
            m1 | m2 == m4,
            m1 & m2 == m5,
            m1 ^ m2 == m6,
            j1 & j2 == j3,
            j1 ^ j2 == j4,
            a - b == {'r', 'd', 'b'},
            a | b == {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'},
            a & b == {'a', 'c'},
            a ^ b == {'r', 'd', 'b', 'm', 'z', 'l'},
            Multiset(m1.to_list()) == Multiset([1,2,2,3,3,3,4,4,4,"five", "five"]),
            ]

run_tests()


Was This Post Helpful? 0
  • +
  • -

#37 jon.kiparsky   User is online

  • Beginner
  • member icon


Reputation: 11134
  • View blog
  • Posts: 19,077
  • Joined: 19-March 11

Re: set operations on sets with more than one of the same member

Posted 12 March 2018 - 10:29 PM

- I would caution against thinking of | and & as the "bitwise" operators in python. True in some languages, but in python they just mean whatever __or__ and __and__ mean. For sets, they mean union and intersection, for ints they mean bitwise and and or, for some class that you make up they mean whatever you make them mean.

- For normalize, I would start with simply self._frq = {key: val for (key, val) in self._frq.items() if val > 0}. Who knows, it might work. :)

- If you want this to be more set-like, you might consider adding add, remove, pop, and the rest of the set methods. There are also the dunder methods, like __bool__ (probably pretty obvious) and __contains__ (possibly less obvious, at least in terms of the user's interaction with it) Depends what you're interested in, really, since this really something you're pursuing for your own pleasure and edification.

- Another suggestion: make the initial list in your __init__ optional, so the no-args constructor produces an empty Multiset. This just seems like the nicer way to treat the user. :)
Was This Post Helpful? 1
  • +
  • -

#38 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 13 March 2018 - 03:06 AM

It was late night, and I knew the 'bitwise' word was incorrect, but thought it would convey the meaning. Your comments acknowledged!

I did include the normalize function (line 89) and call it from __sub__ (line 53). And, yes, it works!

You wrote earlier:

Quote

Little nitpicking, I would usually define the intersection and union and set difference functions and then let the dunder methods call those, but that's a matter of style.

Do you mean me to add a function like 'intersection()' and let it call '__and__' ? This will lead to:
m3 = m1.intersection(m2)

..leading to same result as

m3 = m1 & m2
Why do you think one is better than the other? Except that both exists for normal set.

I will implement some of the set methods as training, but all of them are quite many:
Python Set Methods
Method	                Description

add()	                Add an element to a set
clear()	                Remove all elements form a set
copy()	                Return a shallow copy of a set
difference()	        Return the difference of two or more sets as a new set
difference_update()	Remove all elements of another set from this set
discard()	        Remove an element from set if it is a member. (Do nothing if the element is not in set)
intersection()	        Return the intersection of two sets as a new set
intersection_update()	Update the set with the intersection of itself and another
isdisjoint()	        Return True if two sets have a null intersection
issubset()	        Return True if another set contains this set
issuperset()	        Return True if this set contains another set
pop()	                Remove and return an arbitary set element. Raise KeyError if the set is empty
remove()	        Remove an element from a set. If the element is not a member, raise a KeyError
symmetric_difference()	Return the symmetric difference of two sets as a new set
symmetric_difference_update()	Update a set with the symmetric difference of itself and another
union()	                Return the union of sets in a new set
update()	        Update a set with the union of itself and others


I need to work a little on the code before next version is ready...
Was This Post Helpful? 0
  • +
  • -

#39 DK3250   User is offline

  • Pythonian
  • member icon

Reputation: 420
  • View blog
  • Posts: 1,342
  • Joined: 27-December 13

Re: set operations on sets with more than one of the same member

Posted 13 March 2018 - 01:54 PM

This is my latest version of the Multiset class.

I have included three new functions: add(), pop() and union().

add() will add either:
* a single value in existing key
* a multitude of values in existing key
* a new key, value pair

pop() has a boolean, 'group', that defaults to False.
* as default pop() returns a single value and reduces the Multiset instance accordingly
* with group=True, pop() returns a full {key: value} dict and reducec the Multiset instance

union() extends the Multiset instance with another instance.
* values are updated for existing keys or new 'key: value' pairs are created.

The test has been updated:
add() is tested in f0()
pop() is tested in f1() and f2()
union is tested in f2() and f3()

This whole exercise has been extremely learning.
jon.kiparsky, thank you for pushing me forward and being patient with me.

However, unless you can hint of even more hidden learning points, I will not pursue this to the end i.e. making all the set methods in the Multiset class. I will gladly improve the existing code base, but otherwise I'll stop here.

Current version:
import random

class Multiset():
    """
    Multiset instances are describtions of frequency of occurance in a list.
    
    A number of operations are possible using Multiset instances, actually the
    same operations and operators as for normal set:

    * a - c, returns data in 'a' but not in 'c'
    * a | c, returns data in a or c or both
    * a & c, returns data in both 'a' and 'c'
    * a ^ c, returns data in 'a' or 'c' but not both

    * to_list(), returns an ordinary list
    * freq(), returns data as ordinary dictionary
    * to_set(), returns data as set

    * add(), adds single value, multitudes of single value or new key
    * pop(), pops single value or a key group
    * union(), extends with another Multiset instance

    Made by DK3250
    March 13, 2018
    
    Test functions inspired by jon.kiparsky
    """
    def __init__(self, lst=[]):
        def _frequence(_lst):
            self._frq = {}
            for item in _lst:
                if item in self._frq.keys():
                    self._frq[item] += 1
                else:
                    self._frq[item] = 1
        
        try:
            _lst = list(lst)
        except TypeError:
            print("Illegal input: '{0}'".format(lst))
            print("Argument cannot be converted to list")
            return

        _frequence(_lst)

    def __repr__(self):
        if hasattr(self, '_frq'):
            return "Multiset({0})".format(self.freq())
        else:
            return "Instance does not exist"

    def __sub__(self, other):
        " items in self but not in other "
        result = Multiset([])
        
        for key, value in self._frq.items():
            result._frq[key] = max(0, value - other._frq.get(key, 0))

        result._normalize()
        return result

    def __or__(self, other):
        "items in self or other or both"
        result = Multiset([])

        for key, value in self._frq.items():
            result._frq[key] = value + other._frq.get(key, 0)
                
        for key, value in other._frq.items():
            if key not in self._frq.keys():
                result._frq[key] = value

        return result

    def __and__(self, other):
        "items in both self and other"
        result = Multiset([])

        for key, value in self._frq.items():
            if key in other._frq.keys():
                result._frq[key] = min(value, other._frq[key])

        return result

    def __xor__(self, other):
        "items in self or other but not both"
        a = self | other
        b = self & other
        return a - b - b

    def __eq__(self, other):
        "returns 'True' if data are equal, else 'False'"
        return self._frq == other._frq

    def _normalize(self):
        self._frq = {key: val for key, val in self._frq.items() if val > 0}

    def to_list(self):
        return [key for key, value in self._frq.items() for _ in range(value)]

    def to_set(self):
        return set(self.to_list())

    def freq(self):
        return self._frq

    def add(self, key, value=1):
        self._frq[key] = self._frq.get(key, 0) + value

    def pop(self, group=False):
        k_pop = random.choice(list(self._frq.keys()))
        if group:
            v_pop = self._frq[k_pop]
            self._frq = {key: val for key, val in self._frq.items() if key != k_pop}
            return {k_pop: v_pop}
        else:
            self._frq[k_pop] -= 1
            self._normalize()
            return k_pop

    def union(self, other):
        for key, value in other._frq.items():
            self.add(key, value)


def run_tests():
    failures = []
    for i, test_case in enumerate(all_test, 1):
        try:
            assert test_case
            print (".", end="")  # visual representation of success.
        except Assertionerror:
            failures.append(i)
            print ("F", end="") 
##        except:  # intentional bare except
##            pass
    print("")
    print_test_report(failures) 

def print_test_report(failures):
    if failures:
        print ("You had %d failing tests. Cases %s failed." % (len(failures), ", ".join(map(str, failures))))
    

## TEST
m1 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five"])
m2 = Multiset([1,1,2,"six"])
m3 = Multiset([2,3,3,3,4,4,4,"five", "five"])  # sub
m4 = Multiset([1,2,2,3,3,3,4,4,4,"five", "five",1,1,2,"six"])  # or
m5 = Multiset([1,2])  # and
m6 = Multiset([1,2,3,3,3,4,4,4,"five", "five", "six"])  # xor

j1 = Multiset([1,1,2,3,4,4])
j2 = Multiset([1,2,2,4,4])
j3 = Multiset([1,2,4,4])
j4 = Multiset([1,2,3])

a = set('abracadabra')
b = set('alacazam')

def f0(k, v=1):
    m1.add(k, v)
    return True
    
def f1():
    v = m1.pop()  # pop a value
    m1.add(v)  # return the value back
    return True

def f2():
    d = m1.pop(True)  # pop a group
    d_list = [key for key, value in d.items() for _ in range(value)]
    m1.union(Multiset(d_list))  # return the group
    return True

def f3():
    m1.union(Multiset([6,6,6,6]))
    return True
             
    
     

all_test = [m1 & m2 == m2 & m1,
            m1 - m2 == m3,
            m1 | m2 == m4,
            m1 & m2 == m5,
            m1 ^ m2 == m6,
            j1 & j2 == j3,
            j1 ^ j2 == j4,
            a - b == {'r', 'd', 'b'},
            a | b == {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'},
            a & b == {'a', 'c'},
            a ^ b == {'r', 'd', 'b', 'm', 'z', 'l'},
            Multiset(m1.to_list()) == Multiset([1,2,2,3,3,3,4,4,4,"five", "five"]),
            m1 - m1 == Multiset(),
            f0(1),  # add(1), single value
            m1 == Multiset([1,1,2,2,3,3,3,4,4,4,"five", "five"]),
            f0(1, 2),  # add(1, 2), two of same value
            m1 == Multiset([1,1,1,1,2,2,3,3,3,4,4,4,"five", "five"]),
            f0(5),  # add(5), new key
            m1 == Multiset([1,1,1,1,2,2,3,3,3,4,4,4,"five", "five",5]),
            f1(),  # pop single value
            m1 == Multiset([1,1,1,1,2,2,3,3,3,4,4,4,"five", "five",5]),
            f2(),  # pop key group
            m1 == Multiset([1,1,1,1,2,2,3,3,3,4,4,4,"five", "five",5]),
            f3(),  # extends with new key
            m1 == Multiset([1,1,1,1,2,2,3,3,3,4,4,4,"five", "five",5,6,6,6,6]),
            ]

run_tests()


Was This Post Helpful? 0
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3