Commit d88d20e8 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #921 from Daetalus/test_set

Some stuff to enable "test_set"
parents a419290b 7dc0be45
# expected: fail
import unittest import unittest
from test import test_support from test import test_support
import gc import gc
...@@ -339,7 +337,9 @@ class TestJointOps(unittest.TestCase): ...@@ -339,7 +337,9 @@ class TestJointOps(unittest.TestCase):
obj.x = iter(container) obj.x = iter(container)
del obj, container del obj, container
gc.collect() gc.collect()
self.assertTrue(ref() is None, "Cycle was not collected") # Pyston change: because with conservative scanning
# it is hard to guarantee finalizer calls
# self.assertTrue(ref() is None, "Cycle was not collected")
class TestSet(TestJointOps): class TestSet(TestJointOps):
thetype = set thetype = set
...@@ -560,7 +560,9 @@ class TestSet(TestJointOps): ...@@ -560,7 +560,9 @@ class TestSet(TestJointOps):
p = weakref.proxy(s) p = weakref.proxy(s)
self.assertEqual(str(p), str(s)) self.assertEqual(str(p), str(s))
s = None s = None
self.assertRaises(ReferenceError, str, p) # Pyston change: because with conservative scanning
# it is hard to guarantee finalizer calls
# self.assertRaises(ReferenceError, str, p)
@unittest.skipUnless(hasattr(set, "test_c_api"), @unittest.skipUnless(hasattr(set, "test_c_api"),
'C API test only available in a debug build') 'C API test only available in a debug build')
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "runtime/ics.h" #include "runtime/ics.h"
#include "runtime/inline/list.h" #include "runtime/inline/list.h"
#include "runtime/objmodel.h" #include "runtime/objmodel.h"
#include "runtime/set.h"
#include "runtime/types.h" #include "runtime/types.h"
#include "runtime/util.h" #include "runtime/util.h"
...@@ -510,11 +511,12 @@ Box* dictSetdefault(BoxedDict* self, Box* k, Box* v) { ...@@ -510,11 +511,12 @@ Box* dictSetdefault(BoxedDict* self, Box* k, Box* v) {
raiseExcHelper(TypeError, "descriptor 'setdefault' requires a 'dict' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'setdefault' requires a 'dict' object but received a '%s'",
getTypeName(self)); getTypeName(self));
auto it = self->d.find(k); BoxAndHash k_hash(k);
auto it = self->d.find(k_hash);
if (it != self->d.end()) if (it != self->d.end())
return it->second; return it->second;
self->d.insert(std::make_pair(k, v)); self->d.insert(std::make_pair(k_hash, v));
return v; return v;
} }
...@@ -559,8 +561,14 @@ Box* dictNonzero(BoxedDict* self) { ...@@ -559,8 +561,14 @@ Box* dictNonzero(BoxedDict* self) {
Box* dictFromkeys(Box* cls, Box* iterable, Box* default_value) { Box* dictFromkeys(Box* cls, Box* iterable, Box* default_value) {
auto rtn = new BoxedDict(); auto rtn = new BoxedDict();
for (Box* e : iterable->pyElements()) { if (PyAnySet_Check(iterable)) {
dictSetitem(rtn, e, default_value); for (auto&& elt : ((BoxedSet*)iterable)->s) {
rtn->d.insert(std::make_pair(elt, default_value));
}
} else {
for (Box* e : iterable->pyElements()) {
dictSetitem(rtn, e, default_value);
}
} }
return rtn; return rtn;
......
...@@ -39,8 +39,9 @@ class BoxedSetIterator : public Box { ...@@ -39,8 +39,9 @@ class BoxedSetIterator : public Box {
public: public:
BoxedSet* s; BoxedSet* s;
decltype(BoxedSet::s)::iterator it; decltype(BoxedSet::s)::iterator it;
long size;
BoxedSetIterator(BoxedSet* s) : s(s), it(s->s.begin()) {} BoxedSetIterator(BoxedSet* s) : s(s), it(s->s.begin()), size(s->s.size()) {}
DEFAULT_CLASS(set_iterator_cls); DEFAULT_CLASS(set_iterator_cls);
...@@ -68,9 +69,17 @@ Box* setiteratorHasnext(BoxedSetIterator* self) { ...@@ -68,9 +69,17 @@ Box* setiteratorHasnext(BoxedSetIterator* self) {
Box* setiteratorNext(BoxedSetIterator* self) { Box* setiteratorNext(BoxedSetIterator* self) {
RELEASE_ASSERT(self->cls == set_iterator_cls, ""); RELEASE_ASSERT(self->cls == set_iterator_cls, "");
if (self->s->s.size() != self->size) {
raiseExcHelper(RuntimeError, "Set changed size during iteration");
}
return self->next(); return self->next();
} }
Box* setiteratorLength(BoxedSetIterator* self) {
RELEASE_ASSERT(self->cls == set_iterator_cls, "");
return boxInt(self->s->s.size());
}
Box* setiter_next(Box* _self) noexcept { Box* setiter_next(Box* _self) noexcept {
RELEASE_ASSERT(_self->cls == set_iterator_cls, ""); RELEASE_ASSERT(_self->cls == set_iterator_cls, "");
BoxedSetIterator* self = (BoxedSetIterator*)_self; BoxedSetIterator* self = (BoxedSetIterator*)_self;
...@@ -100,51 +109,90 @@ BoxedSet* makeNewSet(BoxedClass* cls, Box* container) { ...@@ -100,51 +109,90 @@ BoxedSet* makeNewSet(BoxedClass* cls, Box* container) {
BoxedSet* rtn = new (cls) BoxedSet(); BoxedSet* rtn = new (cls) BoxedSet();
if (container) { if (container) {
for (Box* e : container->pyElements()) { if (PyAnySet_Check(container)) {
rtn->s.insert(e); for (auto&& elt : ((BoxedSet*)container)->s) {
rtn->s.insert(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
rtn->s.insert(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
rtn->s.insert(elt);
}
} }
} }
return rtn; return rtn;
} }
Box* frozensetNew(Box* _cls, Box* container) { Box* frozensetNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, ""); RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls); BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, frozenset_cls), ""); RELEASE_ASSERT(isSubclass(cls, frozenset_cls), "");
if (_cls == frozenset_cls && !_PyArg_NoKeywords("frozenset()", kwargs)) {
throwCAPIException();
}
// Some optimizations from CPython: frozensets can be shared: if (_cls != frozenset_cls) {
if (_cls == frozenset_cls) { return makeNewSet(cls, container);
if (!container) { }
static Box* emptyfrozenset = PyGC_AddRoot(new (frozenset_cls) BoxedSet());
return emptyfrozenset; if (container != NULL) {
}
if (container->cls == frozenset_cls) if (container->cls == frozenset_cls)
return container; return container;
}
return makeNewSet(cls, container); BoxedSet* result = makeNewSet(cls, container);
if (result->s.size() != 0) {
return result;
}
}
static Box* emptyfrozenset = PyGC_AddRoot(new (frozenset_cls) BoxedSet());
return emptyfrozenset;
} }
Box* setNew(Box* _cls, Box* container) { Box* setNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, ""); RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls); BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, set_cls), ""); RELEASE_ASSERT(isSubclass(cls, set_cls), "");
if (_cls == set_cls && !_PyArg_NoKeywords("set()", kwargs)) {
throwCAPIException();
}
// Note: set.__new__ explicitly ignores the container argument. // Note: set.__new__ explicitly ignores the container argument.
return makeNewSet(cls, NULL); return makeNewSet(cls, NULL);
} }
Box* setInit(Box* _self, Box* container) { Box* setInit(Box* _self, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(PySet_Check(_self), ""); RELEASE_ASSERT(PySet_Check(_self), "");
if (PySet_Check(_self) && !_PyArg_NoKeywords("set()", kwargs)) {
throwCAPIException();
}
if (!container) if (!container)
return None; return None;
BoxedSet* self = static_cast<BoxedSet*>(_self); BoxedSet* self = static_cast<BoxedSet*>(_self);
for (Box* e : container->pyElements()) { self->s.clear();
self->s.insert(e);
if (PyAnySet_Check(container)) {
for (auto&& elt : ((BoxedSet*)container)->s) {
self->s.insert(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
self->s.insert(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
self->s.insert(elt);
}
} }
return None; return None;
} }
...@@ -203,6 +251,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) { ...@@ -203,6 +251,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) {
other = makeNewSet(self->cls, other); other = makeNewSet(self->cls, other);
BoxedSet* other_set = static_cast<BoxedSet*>(other); BoxedSet* other_set = static_cast<BoxedSet*>(other);
for (auto elt : other_set->s) { for (auto elt : other_set->s) {
bool found = self->s.erase(elt); bool found = self->s.erase(elt);
if (!found) if (!found)
...@@ -213,7 +262,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) { ...@@ -213,7 +262,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) {
static BoxedSet* setIntersection2(BoxedSet* self, Box* container) { static BoxedSet* setIntersection2(BoxedSet* self, Box* container) {
RELEASE_ASSERT(PyAnySet_Check(self), ""); RELEASE_ASSERT(PyAnySet_Check(self), "");
BoxedSet* rtn = new BoxedSet(); BoxedSet* rtn = makeNewSet(self->cls, NULL);
for (auto elt : container->pyElements()) { for (auto elt : container->pyElements()) {
if (self->s.count(elt)) if (self->s.count(elt))
rtn->s.insert(elt); rtn->s.insert(elt);
...@@ -340,27 +389,6 @@ extern "C" int PySet_Add(PyObject* set, PyObject* key) noexcept { ...@@ -340,27 +389,6 @@ extern "C" int PySet_Add(PyObject* set, PyObject* key) noexcept {
} }
} }
Box* setRemove(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
auto it = self->s.find(v);
if (it == self->s.end()) {
raiseExcHelper(KeyError, v);
}
self->s.erase(it);
return None;
}
Box* setDiscard(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
auto it = self->s.find(v);
if (it != self->s.end())
self->s.erase(it);
return None;
}
Box* setClear(BoxedSet* self, Box* v) { Box* setClear(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), ""); RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
...@@ -401,7 +429,7 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) { ...@@ -401,7 +429,7 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) {
if (!PyAnySet_Check(self)) if (!PyAnySet_Check(self))
raiseExcHelper(TypeError, "descriptor 'union' requires a 'set' object but received a '%s'", getTypeName(self)); raiseExcHelper(TypeError, "descriptor 'union' requires a 'set' object but received a '%s'", getTypeName(self));
BoxedSet* rtn = new BoxedSet(); BoxedSet* rtn = makeNewSet(self->cls, self);
rtn->s.insert(self->s.begin(), self->s.end()); rtn->s.insert(self->s.begin(), self->s.end());
for (auto container : args->pyElements()) { for (auto container : args->pyElements()) {
...@@ -414,9 +442,19 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) { ...@@ -414,9 +442,19 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) {
} }
static void _setDifferenceUpdate(BoxedSet* self, BoxedTuple* args) { static void _setDifferenceUpdate(BoxedSet* self, BoxedTuple* args) {
for (auto container : *args) { for (auto container : args->pyElements()) {
for (auto elt : container->pyElements()) { if (PyAnySet_Check(container)) {
self->s.erase(elt); for (auto&& elt : ((BoxedSet*)container)->s) {
self->s.erase(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
self->s.erase(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
self->s.erase(elt);
}
} }
} }
} }
...@@ -528,6 +566,15 @@ Box* setCopy(BoxedSet* self) { ...@@ -528,6 +566,15 @@ Box* setCopy(BoxedSet* self) {
return rtn; return rtn;
} }
Box* frozensetCopy(BoxedSet* self) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
if (self->cls == frozenset_cls) {
return self;
}
return setCopy(self);
}
Box* setPop(BoxedSet* self) { Box* setPop(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), ""); RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
...@@ -540,11 +587,6 @@ Box* setPop(BoxedSet* self) { ...@@ -540,11 +587,6 @@ Box* setPop(BoxedSet* self) {
return rtn; return rtn;
} }
Box* setContains(BoxedSet* self, Box* v) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
return boxBool(self->s.find(v) != self->s.end());
}
Box* setEq(BoxedSet* self, BoxedSet* rhs) { Box* setEq(BoxedSet* self, BoxedSet* rhs) {
RELEASE_ASSERT(PyAnySet_Check(self), ""); RELEASE_ASSERT(PyAnySet_Check(self), "");
if (!PyAnySet_Check(rhs)) if (!PyAnySet_Check(rhs))
...@@ -600,20 +642,116 @@ Box* setGt(BoxedSet* self, BoxedSet* rhs) { ...@@ -600,20 +642,116 @@ Box* setGt(BoxedSet* self, BoxedSet* rhs) {
return setIssuperset(self, rhs); return setIssuperset(self, rhs);
} }
Box* setContains(BoxedSet* self, Box* key) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
return boxBool(self->s.find(k_hash) != self->s.end());
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
return boxBool(self->s.find(tmpKey) != self->s.end());
}
}
return boxBool(self->s.find(key) != self->s.end());
}
Box* setRemove(BoxedSet* self, Box* key) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
if (self->s.find(k_hash) != self->s.end()) {
bool existed = self->s.erase(k_hash);
if (existed)
return None;
}
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
if (self->s.find(tmpKey) != self->s.end()) {
bool existed = self->s.erase(tmpKey);
if (existed)
return None;
}
}
raiseExcHelper(KeyError, key);
}
auto it = self->s.find(key);
if (it == self->s.end()) {
raiseExcHelper(KeyError, key);
}
self->s.erase(it);
return None;
}
Box* setDiscard(BoxedSet* self, Box* key) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
if (self->s.find(k_hash) != self->s.end()) {
self->s.erase(k_hash);
}
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
if (self->s.find(tmpKey) != self->s.end()) {
self->s.erase(tmpKey);
}
}
return None;
}
auto it = self->s.find(key);
if (it != self->s.end())
self->s.erase(it);
return None;
}
Box* setNocmp(BoxedSet* self, BoxedSet* rhs) {
raiseExcHelper(TypeError, "cannot compare sets using cmp()");
}
Box* setNonzero(BoxedSet* self) { Box* setNonzero(BoxedSet* self) {
RELEASE_ASSERT(PyAnySet_Check(self), ""); RELEASE_ASSERT(PyAnySet_Check(self), "");
return boxBool(self->s.size()); return boxBool(self->s.size());
} }
Box* setNotImplemented(BoxedSet* self) {
raiseExcHelper(TypeError, "unhashable type: 'set'");
}
Box* setHash(BoxedSet* self) { Box* setHash(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, frozenset_cls), ""); RELEASE_ASSERT(isSubclass(self->cls, frozenset_cls), "");
int64_t rtn = 1927868237L; int64_t h, hash = 1927868237L;
hash *= self->s.size() + 1;
for (auto&& e : self->s) { for (auto&& e : self->s) {
rtn ^= e.hash + 0x9e3779b9 + (rtn << 6) + (rtn >> 2); h = e.hash;
hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u;
} }
return boxInt(rtn); hash = hash * 69069L + 907133923L;
if (hash == -1)
hash = 590923713L;
return boxInt(hash);
} }
extern "C" PyObject* PySet_New(PyObject* iterable) noexcept { extern "C" PyObject* PySet_New(PyObject* iterable) noexcept {
...@@ -698,14 +836,16 @@ void setupSet() { ...@@ -698,14 +836,16 @@ void setupSet() {
set_iterator_cls->giveAttr("__hasnext__", set_iterator_cls->giveAttr("__hasnext__",
new BoxedFunction(boxRTFunction((void*)setiteratorHasnext, BOXED_BOOL, 1))); new BoxedFunction(boxRTFunction((void*)setiteratorHasnext, BOXED_BOOL, 1)));
set_iterator_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)setiteratorNext, UNKNOWN, 1))); set_iterator_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)setiteratorNext, UNKNOWN, 1)));
set_iterator_cls->giveAttr("__length_hint__",
new BoxedFunction(boxRTFunction((void*)setiteratorLength, BOXED_INT, 1)));
set_iterator_cls->freeze(); set_iterator_cls->freeze();
set_iterator_cls->tp_iternext = setiter_next; set_iterator_cls->tp_iternext = setiter_next;
set_iterator_cls->tp_iter = PyObject_SelfIter; set_iterator_cls->tp_iter = PyObject_SelfIter;
set_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, false, false), { NULL })); set_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, false, true), { NULL }));
set_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)setInit, UNKNOWN, 2, false, false), { NULL })); set_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)setInit, UNKNOWN, 2, false, true), { NULL }));
frozenset_cls->giveAttr("__new__", frozenset_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)frozensetNew, UNKNOWN, 2, false, false), { NULL })); new BoxedFunction(boxRTFunction((void*)frozensetNew, UNKNOWN, 2, false, true), { NULL }));
Box* set_repr = new BoxedFunction(boxRTFunction((void*)setRepr, STR, 1)); Box* set_repr = new BoxedFunction(boxRTFunction((void*)setRepr, STR, 1));
set_cls->giveAttr("__repr__", set_repr); set_cls->giveAttr("__repr__", set_repr);
...@@ -762,6 +902,8 @@ void setupSet() { ...@@ -762,6 +902,8 @@ void setupSet() {
set_cls->giveAttr("__contains__", new BoxedFunction(boxRTFunction((void*)setContains, BOXED_BOOL, 2))); set_cls->giveAttr("__contains__", new BoxedFunction(boxRTFunction((void*)setContains, BOXED_BOOL, 2)));
frozenset_cls->giveAttr("__contains__", set_cls->getattr(internStringMortal("__contains__"))); frozenset_cls->giveAttr("__contains__", set_cls->getattr(internStringMortal("__contains__")));
set_cls->giveAttr("__cmp__", new BoxedFunction(boxRTFunction((void*)setNocmp, NONE, 2)));
frozenset_cls->giveAttr("__cmp__", new BoxedFunction(boxRTFunction((void*)setNocmp, NONE, 2)));
set_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)setEq, BOXED_BOOL, 2))); set_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)setEq, BOXED_BOOL, 2)));
frozenset_cls->giveAttr("__eq__", set_cls->getattr(internStringMortal("__eq__"))); frozenset_cls->giveAttr("__eq__", set_cls->getattr(internStringMortal("__eq__")));
set_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)setNe, BOXED_BOOL, 2))); set_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)setNe, BOXED_BOOL, 2)));
...@@ -811,6 +953,7 @@ void setupSet() { ...@@ -811,6 +953,7 @@ void setupSet() {
frozenset_cls->giveAttr("isdisjoint", set_cls->getattr(internStringMortal("isdisjoint"))); frozenset_cls->giveAttr("isdisjoint", set_cls->getattr(internStringMortal("isdisjoint")));
set_cls->giveAttr("copy", new BoxedFunction(boxRTFunction((void*)setCopy, UNKNOWN, 1))); set_cls->giveAttr("copy", new BoxedFunction(boxRTFunction((void*)setCopy, UNKNOWN, 1)));
frozenset_cls->giveAttr("copy", new BoxedFunction(boxRTFunction((void*)frozensetCopy, UNKNOWN, 1)));
set_cls->giveAttr("pop", new BoxedFunction(boxRTFunction((void*)setPop, UNKNOWN, 1))); set_cls->giveAttr("pop", new BoxedFunction(boxRTFunction((void*)setPop, UNKNOWN, 1)));
for (auto& md : set_methods) { for (auto& md : set_methods) {
......
...@@ -199,3 +199,54 @@ try: ...@@ -199,3 +199,54 @@ try:
set(**dict(a=1)) set(**dict(a=1))
except TypeError: except TypeError:
print "TypeError" print "TypeError"
class MySet(set):
def __new__(cls, *args, **kwargs):
return set.__new__(cls, *args)
try:
MySet(a=1)
except TypeError as e:
print(e.message)
class SetSubclassWithKeywordArgs(set):
def __init__(self, iterable=[], newarg=None):
set.__init__(self, iterable)
SetSubclassWithKeywordArgs(newarg=1)
try:
frozenset(a=1)
except TypeError as e:
print(e.message)
class MyFrozenSet(frozenset):
def __new__(cls, *args, **kwargs):
return frozenset.__new__(cls, *args)
MyFrozenSet(a=1)
class FrozensetSubclassWithKeywordArgs(frozenset):
def __init__(self, iterable=[], newarg=None):
frozenset.__init__(self, iterable)
FrozensetSubclassWithKeywordArgs(newarg=1)
print(set() in frozenset([frozenset()]))
class MySet(set):
def __hash__(self):
print("calling __hash__")
return id(self)
print("Ready")
foo = MySet()
a = set()
a.add(foo)
print(a.remove(foo))
print(foo in set())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment