18
18
from fastcore .dispatch import retain_type
19
19
20
20
# %% ../nbs/00_core.ipynb 6
21
- def _is_tuple (o ): return isinstance (o , tuple ) and not hasattr (o , '_fields' )
21
+ def _get_name (o ):
22
+ if hasattr (o ,'__qualname__' ): return o .__qualname__
23
+ if hasattr (o ,'__name__' ): return o .__name__
24
+ return o .__class__ .__name__
22
25
23
26
# %% ../nbs/00_core.ipynb 7
27
+ def _is_tuple (o ): return isinstance (o , tuple ) and not hasattr (o , '_fields' )
28
+
29
+ # %% ../nbs/00_core.ipynb 8
24
30
def retain_type (new , old , ret_type ,as_copy = False ):
25
31
if new is None : return new
26
32
if ret_type is NoneType : return new
@@ -34,7 +40,7 @@ def retain_type(new, old, ret_type,as_copy=False):
34
40
return retain_meta (old , cast (new , ret_type ), as_copy = as_copy )
35
41
36
42
37
- # %% ../nbs/00_core.ipynb 14
43
+ # %% ../nbs/00_core.ipynb 15
38
44
_tfm_methods = 'encodes' ,'decodes' ,'setups'
39
45
40
46
def _is_tfm_method (n , f ): return n in _tfm_methods and callable (f )
@@ -46,91 +52,78 @@ def __setitem__(self, k, v):
46
52
self [k ].dispatch (v )
47
53
48
54
49
- # %% ../nbs/00_core.ipynb 15
55
+ # %% ../nbs/00_core.ipynb 16
50
56
class _TfmMeta (type ):
51
57
@classmethod
52
58
def __prepare__ (cls , name , bases ):
53
59
return _TfmDict ()
54
60
55
- # %% ../nbs/00_core.ipynb 18
61
+ # %% ../nbs/00_core.ipynb 20
56
62
def _has_self_arg (f ) -> bool :
57
63
"Check if function `f` has 'self' as first parameter"
58
64
try : return f .__code__ .co_varnames [0 ] == 'self'
59
65
# Attribute error if not callable
60
66
# IndexError if no (kw)args
61
67
except (AttributeError , IndexError ): return False
62
68
63
- # %% ../nbs/00_core.ipynb 19
69
+ # %% ../nbs/00_core.ipynb 21
64
70
def _subclass_decorator (cls , f ):
65
71
nm = f .__name__
66
- # needed for plum to register dispatch correctly
67
- # f.__qualname__ = f"{cls.__name__}.{nm}"
68
72
if not hasattr (cls , nm ): setattr (cls , nm , Function (f ).dispatch (f ))
69
73
else : getattr (cls ,nm ).dispatch (f )
70
74
return cls
71
75
72
- # %% ../nbs/00_core.ipynb 20
76
+ # %% ../nbs/00_core.ipynb 22
73
77
class Transform (metaclass = _TfmMeta ):
74
78
"Delegates (`__call__`,`decode`,`setup`) to (<code>encodes</code>,<code>decodes</code>,<code>setups</code>) if `split_idx` matches"
75
79
split_idx ,init_enc ,order ,train_setup = None ,None ,0 ,None
76
-
80
+
77
81
def __init_subclass__ (cls ):
78
82
orig_init = cls .__init__ if hasattr (cls ,'__init__' ) else lambda o : None
79
-
80
83
def __init__ (self ,* args ,** kwargs ):
81
84
orig_init (self , * args , ** kwargs )
82
85
for nm in _tfm_methods :
83
86
if hasattr (self .__class__ , nm ):
84
87
setattr (self , nm , MethodType (getattr (self .__class__ , nm ), self ))
85
-
86
88
cls .__init__ = __init__
87
89
88
90
def __new__ (cls , enc = None , dec = None ):
89
- # subclass of Transform decorator usage
91
+ "Catch use of subclasses of Transform as decorator."
90
92
if (
91
93
issubclass (cls ,Transform ) and
92
94
_has_self_arg (enc ) and
93
95
enc .__name__ in _tfm_methods and
94
96
dec is None
95
97
): return _subclass_decorator (cls , enc )
96
- # default usecase
97
98
return super ().__new__ (cls )
98
99
99
100
def __init__ (self ,enc = None ,dec = None , split_idx = None , order = None ):
100
101
self .split_idx = ifnone (split_idx , self .split_idx )
101
- if order is not None : self .order = order
102
-
102
+ if order is not None : self .order = order
103
103
enc = L (enc )
104
104
if enc : self .encodes = Function (enc [0 ])
105
105
for e in enc : self .encodes .dispatch (e )
106
-
107
106
dec = L (dec )
108
107
if dec : self .decodes = Function (dec [0 ])
109
108
for d in dec : self .decodes .dispatch (d )
110
109
111
- def __call__ (self ,* args ,split_idx = None , ** kwargs ):
112
- return self ._call ('encodes' , split_idx , * args ,** kwargs )
113
-
114
- def decode (self , * args ,split_idx = None , ** kwargs ):
115
- return self ._call ('decodes' , split_idx , * args , ** kwargs )
116
-
110
+ def __call__ (self ,* args ,split_idx = None , ** kwargs ): return self ._call ('encodes' , split_idx , * args ,** kwargs )
111
+ def decode (self , * args ,split_idx = None , ** kwargs ): return self ._call ('decodes' , split_idx , * args , ** kwargs )
117
112
def setup (self , items = None , train_setup = False ):
118
113
train_setup = train_setup if self .train_setup is None else self .train_setup
119
- # fastcore could initalize a TypeDispatch without implementation, always returning input
120
114
setups = self .setups if hasattr (self ,'setups' ) else lambda o :o
121
115
return setups (getattr (items , 'train' , items ) if train_setup else items )
122
116
123
117
def _call (self , nm , split_idx = None , * args , ** kwargs ):
124
118
if split_idx != self .split_idx and self .split_idx is not None : return args [0 ]
125
119
return self ._do_call (nm , * args , ** kwargs )
126
-
120
+
127
121
def _do_call (self , nm , * args , ** kwargs ):
128
122
x = args [0 ]
129
123
if not hasattr (self , nm ): return x
130
124
if _is_tuple (x ):
131
125
res = tuple (self ._do_call (nm , x_ , * args [1 :], ** kwargs ) for x_ in x )
132
126
return retain_type (res , x , Any )
133
-
134
127
f_args = args if type (self ) is Transform else (self ,)+ args
135
128
try :
136
129
method , ret_type = getattr (self ,nm )._resolve_method_with_cache (f_args )
@@ -139,9 +132,13 @@ def _do_call(self, nm, *args, **kwargs):
139
132
res = method (* f_args ,** kwargs )
140
133
return retain_type (res , x , ret_type )
141
134
135
+ @property
136
+ def name (self ): return getattr (self , '_name' , _get_name (self ))
137
+ def __repr__ (self ): return f'{ self .name } :\n encodes: { self .encodes } decodes: { self .decodes } '
138
+
142
139
add_docs (Transform , decode = "Delegate to decodes to undo transform" , setup = "Delegate to setups to set up transform" )
143
140
144
- # %% ../nbs/00_core.ipynb 116
141
+ # %% ../nbs/00_core.ipynb 117
145
142
def compose_tfms (x , tfms , is_enc = True , reverse = False , ** kwargs ):
146
143
"Apply all `func_nm` attribute of `tfms` on `x`, maybe in `reverse` order"
147
144
if reverse : tfms = reversed (tfms )
@@ -151,13 +148,13 @@ def compose_tfms(x, tfms, is_enc=True, reverse=False, **kwargs):
151
148
return x
152
149
153
150
154
- # %% ../nbs/00_core.ipynb 121
151
+ # %% ../nbs/00_core.ipynb 122
155
152
def mk_transform (f ):
156
153
"Convert function `f` to `Transform` if it isn't already one"
157
154
f = instantiate (f )
158
155
return f if isinstance (f ,(Transform ,Pipeline )) else Transform (f )
159
156
160
- # %% ../nbs/00_core.ipynb 122
157
+ # %% ../nbs/00_core.ipynb 123
161
158
def gather_attrs (o , k , nm ):
162
159
"Used in __getattr__ to collect all attrs `k` from `self.{nm}`"
163
160
if k .startswith ('_' ) or k == nm : raise AttributeError (k )
@@ -166,12 +163,12 @@ def gather_attrs(o, k, nm):
166
163
if not res : raise AttributeError (k )
167
164
return res [0 ] if len (res )== 1 else L (res )
168
165
169
- # %% ../nbs/00_core.ipynb 123
166
+ # %% ../nbs/00_core.ipynb 124
170
167
def gather_attr_names (o , nm ):
171
168
"Used in __dir__ to collect all attrs `k` from `self.{nm}`"
172
169
return L (getattr (o ,nm )).map (dir ).concat ().unique ()
173
170
174
- # %% ../nbs/00_core.ipynb 124
171
+ # %% ../nbs/00_core.ipynb 125
175
172
class Pipeline :
176
173
"A pipeline of composed (for encode/decode) transforms, setup with types"
177
174
def __init__ (self , funcs = None , split_idx = None ):
0 commit comments