Skip to content

Commit e197dad

Browse files
committed
Use simpler .relation_map() method
The .relation_map() method is like .relations(), but the keys are relation objects and map 1-to-1 to targets. This commit also removes the .source() and .target() methods as they didn't work in common situations, as well as the .incoming_relation() method as it was not favored. This commit also adds a new utility function and associated unit tests.
1 parent 6b7aed9 commit e197dad

File tree

5 files changed

+117
-121
lines changed

5 files changed

+117
-121
lines changed

tests/_util_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
from wn._util import flatten, unique_list
3+
4+
5+
def test_flatten():
6+
assert flatten([]) == []
7+
assert flatten([[]]) == []
8+
assert flatten([[], []]) == []
9+
assert flatten([[[], []], [[], []]]) == [[], [], [], []]
10+
assert flatten([[1]]) == [1]
11+
assert flatten([[1, 2], [3, 4]]) == [1, 2, 3, 4]
12+
assert flatten(["AB", "CD"]) == ["A", "B", "C", "D"]
13+
14+
15+
def test_unique_list():
16+
assert unique_list([]) == []
17+
assert unique_list([1]) == [1]
18+
assert unique_list([1, 1, 1, 1, 1]) == [1]
19+
assert unique_list([1, 1, 2, 2, 1]) == [1, 2]
20+
assert unique_list([2, 1, 2, 2, 1]) == [2, 1]
21+
assert unique_list("A") == ["A"]
22+
assert unique_list("AAA") == ["A"]
23+
assert unique_list("ABABA") == ["A", "B"]
24+
assert unique_list([(1, 2), (1, 2), (2, 3)]) == [(1, 2), (2, 3)]

wn/_core.py

Lines changed: 65 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
NormalizeFunction,
1212
LemmatizeFunction,
1313
)
14-
from wn._util import normalize_form
14+
from wn._util import normalize_form, unique_list
1515
from wn._db import NON_ROWID
1616
from wn._queries import (
1717
find_lexicons,
@@ -487,12 +487,6 @@ def __repr__(self) -> str:
487487
+ f"({self.name!r}, {self.source_id!r}, {self.target_id!r})"
488488
)
489489

490-
def source(self) -> '_Relatable':
491-
raise NotImplementedError
492-
493-
def target(self) -> '_Relatable':
494-
raise NotImplementedError
495-
496490
def metadata(self) -> Metadata:
497491
"""Return the synset's metadata."""
498492
return get_metadata(self._id, self._ENTITY_TYPE)
@@ -501,53 +495,30 @@ def metadata(self) -> Metadata:
501495
class SynsetRelation(_Relation):
502496
_ENTITY_TYPE = _EntityType.SYNSET_RELATIONS
503497

504-
def source(self) -> 'Synset':
505-
return self._wordnet.synset(id=self.source_id)
506-
507-
def target(self) -> 'Synset':
508-
return self._wordnet.synset(id=self.target_id)
509-
510498

511499
class SenseRelation(_Relation):
512500
_ENTITY_TYPE = _EntityType.SENSE_RELATIONS
513501

514-
def source(self) -> 'Sense':
515-
return self._wordnet.sense(id=self.source_id)
516-
517-
def target(self) -> 'Sense':
518-
return self._wordnet.sense(id=self.target_id)
519-
520502

521503
class SenseSynsetRelation(_Relation):
522504
_ENTITY_TYPE = _EntityType.SENSE_SYNSET_RELATIONS
523505

524-
def source(self) -> 'Sense':
525-
return self._wordnet.sense(id=self.source_id)
526-
527-
def target(self) -> 'Synset':
528-
return self._wordnet.synset(id=self.target_id)
529-
530506

531507
T = TypeVar('T', bound='_Relatable')
532508

533509

534510
class _Relatable(_LexiconElement):
535-
__slots__ = 'id', '_in_rel'
511+
__slots__ = 'id',
536512

537513
def __init__(
538514
self,
539515
id: str,
540516
_lexid: int = NON_ROWID,
541517
_id: int = NON_ROWID,
542-
_in_rel: Optional[_Relation] = None,
543518
_wordnet: Optional['Wordnet'] = None
544519
):
545520
super().__init__(_lexid=_lexid, _id=_id, _wordnet=_wordnet)
546521
self.id = id
547-
self._in_rel = _in_rel
548-
549-
def incoming_relation(self) -> Optional[_Relation]:
550-
return self._in_rel
551522

552523
def relations(self: T, *args: str) -> dict[str, list[T]]:
553524
raise NotImplementedError
@@ -598,21 +569,16 @@ class Synset(_Relatable):
598569

599570
_ENTITY_TYPE = _EntityType.SYNSETS
600571

601-
_in_rel: Union[SynsetRelation, SenseSynsetRelation, None]
602-
603572
def __init__(
604573
self,
605574
id: str,
606575
pos: str,
607576
ili: Optional[str] = None,
608577
_lexid: int = NON_ROWID,
609578
_id: int = NON_ROWID,
610-
_in_rel: Union[SynsetRelation, SenseSynsetRelation, None] = None,
611579
_wordnet: Optional['Wordnet'] = None,
612580
):
613-
super().__init__(
614-
id=id, _lexid=_lexid, _id=_id, _in_rel=_in_rel, _wordnet=_wordnet
615-
)
581+
super().__init__(id=id, _lexid=_lexid, _id=_id, _wordnet=_wordnet)
616582
self.pos = pos
617583
self._ili = ili
618584

@@ -622,12 +588,9 @@ def empty(
622588
id: str,
623589
ili: Optional[str] = None,
624590
_lexid: int = NON_ROWID,
625-
_in_rel: Union[SynsetRelation, SenseSynsetRelation, None] = None,
626591
_wordnet: Optional['Wordnet'] = None
627592
):
628-
return cls(
629-
id, pos='', ili=ili, _lexid=_lexid, _in_rel=_in_rel, _wordnet=_wordnet
630-
)
593+
return cls(id, pos='', ili=ili, _lexid=_lexid, _wordnet=_wordnet)
631594

632595
@property
633596
def ili(self):
@@ -722,9 +685,6 @@ def lemmas(self) -> list[Form]:
722685
"""
723686
return [w.lemma() for w in self.words()]
724687

725-
def incoming_relation(self) -> Union[SynsetRelation, SenseSynsetRelation, None]:
726-
return self._in_rel
727-
728688
def relations(self, *args: str) -> dict[str, list['Synset']]:
729689
"""Return a mapping of relation names to lists of synsets.
730690
@@ -746,15 +706,12 @@ def relations(self, *args: str) -> dict[str, list['Synset']]:
746706
hyponym [['coat button'], ['shirt button']]
747707
748708
"""
749-
d: dict[str, list[Synset]] = {}
750-
for ss in self.get_related(*args):
751-
if relation := ss.incoming_relation():
752-
relname = relation.name
753-
if relname in d:
754-
d[relname].append(ss)
755-
else:
756-
d[relname] = [ss]
757-
return d
709+
# inner dict is used as an order-preserving set
710+
relmap: dict[str, dict[Synset, bool]] = {}
711+
for relation, synset in self._iter_relations(*args):
712+
relmap.setdefault(relation.name, {})[synset] = True
713+
# now convert inner dicts to lists
714+
return {relname: list(ss_dict) for relname, ss_dict in relmap.items()}
758715

759716
def get_related(self, *args: str) -> list['Synset']:
760717
"""Return the list of related synsets.
@@ -774,36 +731,44 @@ def get_related(self, *args: str) -> list['Synset']:
774731
>>> [ss.lemmas() for ss in fulcrum.get_related()]
775732
[['pin', 'pivot'], ['lever']]
776733
"""
777-
targets: list[Synset] = []
734+
return unique_list(synset for _, synset in self._iter_relations(*args))
735+
736+
def relation_map(self) -> dict[SynsetRelation, 'Synset']:
737+
return dict(self._iter_relations())
738+
739+
def _iter_relations(self, *args: str) -> Iterator[tuple[SynsetRelation, 'Synset']]:
778740
# first get relations from the current lexicon(s)
779741
if self._id != NON_ROWID:
780-
targets.extend(self._get_local_relations(args))
742+
yield from self._iter_local_relations(args)
781743
# then attempt to expand via ILI
782744
if self._ili is not None and self._wordnet._expanded_ids:
783-
targets.extend(self._get_expanded_relations(args))
784-
return targets
745+
yield from self._iter_expanded_relations(args)
785746

786-
def _get_local_relations(self, args: Sequence[str]) -> Iterator['Synset']:
747+
def _iter_local_relations(
748+
self,
749+
args: Sequence[str],
750+
) -> Iterator[tuple[SynsetRelation, 'Synset']]:
787751
_wn = self._wordnet
788752
lexids = self._get_lexicon_ids()
789753
iterable = get_synset_relations({self._id}, args, lexids)
790754
for relname, rellexid, relrowid, _, ssid, pos, ili, lexid, rowid in iterable:
791-
if lexid not in lexids:
792-
continue
793755
synset_rel = SynsetRelation(
794756
relname, self.id, ssid, rellexid, relrowid, _wordnet=_wn
795757
)
796-
yield Synset(
758+
synset = Synset(
797759
ssid,
798760
pos,
799761
ili,
800762
_lexid=lexid,
801763
_id=rowid,
802-
_in_rel=synset_rel,
803764
_wordnet=_wn
804765
)
766+
yield synset_rel, synset
805767

806-
def _get_expanded_relations(self, args: Sequence[str]) -> Iterator['Synset']:
768+
def _iter_expanded_relations(
769+
self,
770+
args: Sequence[str],
771+
) -> Iterator[tuple[SynsetRelation, 'Synset']]:
807772
_wn = self._wordnet
808773
lexids = self._get_lexicon_ids()
809774
expids = self._wordnet._expanded_ids
@@ -825,17 +790,17 @@ def _get_expanded_relations(self, args: Sequence[str]) -> Iterator['Synset']:
825790
)
826791
local_ss_rows = list(get_synsets_for_ilis([ili], lexicon_rowids=lexids))
827792

828-
for row in local_ss_rows:
829-
yield Synset(*row, _in_rel=synset_rel, _wordnet=_wn)
830-
831-
if not local_ss_rows:
832-
yield Synset.empty(
793+
if local_ss_rows:
794+
for row in local_ss_rows:
795+
yield synset_rel, Synset(*row, _wordnet=_wn)
796+
else:
797+
synset = Synset.empty(
833798
id=_INFERRED_SYNSET,
834799
ili=ili,
835800
_lexid=self._lexid,
836-
_in_rel=synset_rel,
837801
_wordnet=_wn,
838802
)
803+
yield synset_rel, synset
839804

840805
def hypernym_paths(self, simulate_root: bool = False) -> list[list['Synset']]:
841806
"""Return the list of hypernym paths to a root synset."""
@@ -980,21 +945,16 @@ class Sense(_Relatable):
980945

981946
_ENTITY_TYPE = _EntityType.SENSES
982947

983-
_in_rel: Optional[SenseRelation]
984-
985948
def __init__(
986949
self,
987950
id: str,
988951
entry_id: str,
989952
synset_id: str,
990953
_lexid: int = NON_ROWID,
991954
_id: int = NON_ROWID,
992-
_in_rel: Optional[SenseRelation] = None,
993955
_wordnet: Optional['Wordnet'] = None
994956
):
995-
super().__init__(
996-
id=id, _lexid=_lexid, _id=_id, _in_rel=_in_rel, _wordnet=_wordnet
997-
)
957+
super().__init__(id=id, _lexid=_lexid, _id=_id, _wordnet=_wordnet)
998958
self._entry_id = entry_id
999959
self._synset_id = synset_id
1000960

@@ -1061,9 +1021,6 @@ def metadata(self) -> Metadata:
10611021
"""Return the sense's metadata."""
10621022
return get_metadata(self._id, 'senses')
10631023

1064-
def incoming_relation(self) -> Optional[SenseRelation]:
1065-
return self._in_rel
1066-
10671024
def relations(self, *args: str) -> dict[str, list['Sense']]:
10681025
"""Return a mapping of relation names to lists of senses.
10691026
@@ -1076,15 +1033,12 @@ def relations(self, *args: str) -> dict[str, list['Sense']]:
10761033
senses.
10771034
10781035
"""
1079-
d: dict[str, list[Sense]] = {}
1080-
for s in self.get_related(*args):
1081-
if relation := s.incoming_relation():
1082-
relname = relation.name
1083-
if relname in d:
1084-
d[relname].append(s)
1085-
else:
1086-
d[relname] = [s]
1087-
return d
1036+
# inner dict is used as an order-preserving set
1037+
relmap: dict[str, dict[Sense, bool]] = {}
1038+
for relation, sense in self._iter_sense_relations(*args):
1039+
relmap.setdefault(relation.name, {})[sense] = True
1040+
# now convert inner dicts to lists
1041+
return {relname: list(s_dict) for relname, s_dict in relmap.items()}
10881042

10891043
def get_related(self, *args: str) -> list['Sense']:
10901044
"""Return a list of related senses.
@@ -1103,35 +1057,38 @@ def get_related(self, *args: str) -> list['Sense']:
11031057
incoherent
11041058
11051059
"""
1106-
lexids = self._get_lexicon_ids()
1107-
iterable = get_sense_relations(self._id, args, lexids)
1108-
targets: list['Sense'] = []
1109-
for relname, rellexid, relrowid, sid, eid, ssid, lexid, rowid in iterable:
1110-
if lexid not in lexids:
1111-
continue
1112-
sense_rel = SenseRelation(
1060+
return unique_list(sense for _, sense in self._iter_sense_relations(*args))
1061+
1062+
def get_related_synsets(self, *args: str) -> list[Synset]:
1063+
"""Return a list of related synsets."""
1064+
return unique_list(
1065+
synset for _, synset in self._iter_sense_synset_relations(*args)
1066+
)
1067+
1068+
def _iter_sense_relations(self, *args: str) -> Iterator[tuple[SenseRelation, 'Sense']]:
1069+
iterable = get_sense_relations(self._id, args, self._get_lexicon_ids())
1070+
for relname, rellexid, relrowid, _, sid, eid, ssid, lexid, rowid in iterable:
1071+
relation = SenseRelation(
11131072
relname, self.id, sid, rellexid, relrowid, _wordnet=self._wordnet
11141073
)
1115-
target = Sense(
1116-
sid, eid, ssid, lexid, rowid, _in_rel=sense_rel, _wordnet=self._wordnet
1074+
sense = Sense(
1075+
sid, eid, ssid, lexid, rowid, _wordnet=self._wordnet
11171076
)
1118-
targets.append(target)
1119-
return targets
1077+
yield relation, sense
11201078

1121-
def get_related_synsets(self, *args: str) -> list[Synset]:
1122-
"""Return a list of related synsets."""
1123-
lexids = self._get_lexicon_ids()
1124-
iterable = get_sense_synset_relations(self._id, args, lexids)
1125-
targets: list[Synset] = []
1126-
for relname, rellexid, relrowid, _, ssid, pos, ili, lexid, rowid in iterable:
1079+
def _iter_sense_synset_relations(
1080+
self,
1081+
*args: str,
1082+
) -> Iterator[tuple[SenseSynsetRelation, 'Synset']]:
1083+
iterable = get_sense_synset_relations(self._id, args, self._get_lexicon_ids())
1084+
for relname, rellexid, relrowid, _, sid, pos, ili, lexid, rowid in iterable:
11271085
relation = SenseSynsetRelation(
11281086
relname, self.id, ssid, rellexid, relrowid, _wordnet=self._wordnet
11291087
)
1130-
target = Synset(
1131-
ssid, pos, ili, lexid, rowid, _in_rel=relation, _wordnet=self._wordnet
1088+
synset = Synset(
1089+
ssid, pos, ili, lexid, rowid, _wordnet=self._wordnet
11321090
)
1133-
targets.append(target)
1134-
return targets
1091+
yield relation, synset
11351092

11361093
def translate(
11371094
self,

wn/_export.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ def _export_sense_relations(
213213
{'target': id,
214214
'relType': type,
215215
'meta': _export_metadata(rowid, 'sense_relations')}
216-
for type, _, rowid, id, *_
216+
for type, _, rowid, _, id, *_
217217
in get_sense_relations(sense_rowid, '*', lexids)
218218
]
219219
relations.extend(

0 commit comments

Comments
 (0)