Skip to content

Commit 4683a48

Browse files
committed
fix for lazy
1 parent 8681d8c commit 4683a48

File tree

2 files changed

+100
-8
lines changed

2 files changed

+100
-8
lines changed
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import networkx as nx
99
import numpy as np
1010

11+
from ._lazy import lazy
12+
1113
if TYPE_CHECKING:
1214
import pandas as pd
1315

@@ -363,11 +365,11 @@ def remove_node(self, n):
363365
super().add_edge(h, k[1], **attrs)
364366
self._clear_caches()
365367

366-
@property
368+
@lazy
367369
def topological_sorted(self):
368370
return list(reverse_lexicographical_topological_sort(self))
369371

370-
@property
372+
@lazy
371373
def topological_sorted_no_elementals(self):
372374
try:
373375
result = self.topological_sorted.copy()
@@ -392,14 +394,14 @@ def topological_sorted_no_elementals(self):
392394
to_remove.add(code)
393395
return [i for i in result if i not in to_remove]
394396

395-
@property
397+
@lazy
396398
def standard_sort(self):
397399
return self.elementals + tuple(self.topological_sorted_no_elementals)
398400

399401
def node_name(self, code):
400402
return self.nodes[code].get("name", str(code))
401403

402-
@property
404+
@lazy
403405
def standard_sort_names(self):
404406
return [self.node_name(s) for s in self.standard_sort]
405407

@@ -409,7 +411,7 @@ def node_names(self):
409411
def elemental_names(self):
410412
return {s: (self.node_name(s) or s) for s in self.elementals}
411413

412-
@property
414+
@lazy
413415
def standard_slot_map(self):
414416
return {i: n for n, i in enumerate(self.standard_sort)}
415417

@@ -436,7 +438,7 @@ def __elementals_iter(self):
436438
if not out_degree:
437439
yield code
438440

439-
@property
441+
@lazy
440442
def elementals(self):
441443
result = []
442444
found = set()
@@ -507,7 +509,7 @@ def nodes_with_multiple_predecessors_iter(self):
507509
if in_degree > 1:
508510
yield code
509511

510-
@property
512+
@lazy
511513
def standard_competitive_edge_list(self):
512514
alphas = []
513515
for n in self.nodes_with_multiple_predecessors_iter():
@@ -516,7 +518,7 @@ def standard_competitive_edge_list(self):
516518
alphas.append((k, n))
517519
return alphas
518520

519-
@property
521+
@lazy
520522
def standard_competitive_edge_list_2(self):
521523
alphas = []
522524
for n in self.nodes_with_multiple_predecessors_iter():
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# from: https://github.com/stefanholek/lazy
2+
#
3+
# Copyright (c) 2011-2017 Stefan H. Holek
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions
8+
# are met:
9+
#
10+
# 1. Redistributions of source code must retain the above copyright
11+
# notice, this list of conditions and the following disclaimer.
12+
# 2. Redistributions in binary form must reproduce the above copyright
13+
# notice, this list of conditions and the following disclaimer in the
14+
# documentation and/or other materials provided with the distribution.
15+
#
16+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26+
# SUCH DAMAGE.
27+
28+
29+
"""Decorator to create lazy attributes."""
30+
31+
from __future__ import annotations
32+
33+
import functools
34+
35+
36+
class lazy:
37+
"""
38+
Lazy descriptor.
39+
40+
Used as a decorator to create lazy attributes. Lazy attributes
41+
are evaluated on first use.
42+
"""
43+
44+
def __init__(self, func):
45+
self.__func = func
46+
functools.wraps(self.__func)(self)
47+
48+
def __get__(self, inst, inst_cls):
49+
if inst is None:
50+
return self
51+
52+
if not hasattr(inst, "__dict__"):
53+
raise AttributeError(
54+
f"'{inst_cls.__name__}' object has no attribute '__dict__'"
55+
)
56+
57+
name = self.__name__
58+
if name.startswith("__") and not name.endswith("__"):
59+
name = f"_{inst_cls.__name__}{name}"
60+
61+
value = self.__func(inst)
62+
inst.__dict__[name] = value
63+
return value
64+
65+
@classmethod
66+
def invalidate(cls, inst, name):
67+
"""
68+
Invalidate a lazy attribute.
69+
70+
This obviously violates the lazy contract. A subclass of lazy
71+
may however have a contract where invalidation is appropriate.
72+
"""
73+
# These commands from the original are not currently used by larch and
74+
# are omitted for speed. They are left here in case they are needed
75+
# for future compatibility.
76+
#
77+
# inst_cls = inst.__class__
78+
#
79+
# if not hasattr(inst, '__dict__'):
80+
# raise AttributeError("'%s' object has no attribute '__dict__'" % (inst_cls.__name__,))
81+
#
82+
# if name.startswith('__') and not name.endswith('__'):
83+
# name = '_%s%s' % (inst_cls.__name__, name)
84+
#
85+
# if not isinstance(getattr(inst_cls, name), cls):
86+
# raise AttributeError("'%s.%s' is not a %s attribute"
87+
# % (inst_cls.__name__, name, cls.__name__))
88+
89+
if name in inst.__dict__:
90+
del inst.__dict__[name]

0 commit comments

Comments
 (0)