Comments by "Dr Gamma D" (@DrDeuteron) on "Indently"
channel.
-
39
-
37
-
31
-
29
-
26
-
23
-
21
-
20
-
14
-
10
-
10
-
10
-
9
-
9
-
9
-
slice is fantastic. I have a complicated space instrument with around 500 channels, and it comes in so fast the electronics can't keep up, so there are 4 separate amplifier chains...which are all identical. but of course they aren't, so each needs to be calibrated separately, The guys were doing it in Matlab with handwritten if chain=='north': idx = [1, 5, ...] else./...repeated everywhere.
So I enlightened them (oh, and it had a time stamp in position 0), so I just made a dictionary of slice object vs amplifier chain (named after the cardinal directions):
AMP = dict(time=slice(0,1), north=slice(1, None, 4), east=slice(2, None, 4), south=(3, None, 4), west=(4, None, 4))
so a
calibration = {amp: sensor.data[AMP[amp]].mean() for amp in ('north', 'east', 'south', 'west')}
gave me a dictionary of time stamped calibration sample averages (when the system was in calibration mode). Then you save that and scale environmental data until the next cal measurment 2 seconds later.
`No ifs, no loops. cyclomatic complexity minimized. Nothing is WETT (write everything twice), it's all DRY (don't repeat yourself).
9
-
9
-
8
-
8
-
8
-
7
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
6
-
5
-
5
-
when I write code that is a tool, let's say something about LEO assets... when python goes wrong, I throw a builtin exception, (and if it;s I/O, I import errors and figure out which I/O thing went wrong), but if it's a domain specific user mistake, I send a custom exception. for example, they set orbit altitude to 2000 km, I'll throw a:
>>>class LEOAltitude(ValueError):
telling them LEO is 200 - 1600 km.
If it's more general, I may go nuts and have an mro that looks like:
LEOAltitudeError [or MEO, GEO]
AltitudeError. [or Inclination, RightAscension, Eccentricity, MeanAnomaly, ...]
OrbitParameterError [or Epoch or Classification... any thing in aTwo Line Element]
ValueError. [now it's python's mro from here on out]
StandardError
Exception
BaseException
object
5
-
5
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
3
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
so I get massive data files from each orbit of a satellite. Since it orbits 9 times per day, I want to create a "daily output". This could be a real pain. I've seen the dog crap Matlab code ppl have tried: it's unmaintable. So I overload "<<" to concatenate. Since the orbits are downland asynchronously at Thule, Macurdo, Kwagilon [sic], they are out of order, so in one line I fix it all and create a day of data in order to help support our national defense:
day = reduce(operator.lshift, sorted( [orbit3, orbit1, ..., orbit9], key=partial(getattr, 'star time')))
so sweet. The fortran code (yes, we still use it in space), is like 300 lines.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I use classmethods all the time, esp. as constructor helpers...since I never do work in __init__, that method is for setting instance attributes, and that is it. If I need to, say make the instance from a file...I am not reading a file in init, rather:
@classmethod
def fromfile(cls, filename):
with open(filename, 'r') as fsrc:
return cls(*some_function(fsrc.readlines())
and I get a new instance with the file data loaded.
For static methods, say I have a class whose attributes are different real time series, well if I want an fft or something:
@staticmethod
def rfft(x_i):
return np.fft.rfff(x_i)
the reason I put it in class is that I want the object to be able to do everything that needs to be done to it to be part of the interface. I don't want a user looking for np.ftt.rftt and doing it themselves, or making the mistake of using a full fft for complex inputs. A class should contain ALL the functions you would want to use on it,,,even if they don't depend on instance/class attributes.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
@tinahalder8416 Imma disagree with that. He called it a "two-dimensional list", and then later, "the array". lists don't have dimensions, just "len", and they're not arrays. If your data model is an array, use an array. If your data model is a mutable ordered collection of whatever, use a list. If it immutable, use a tuple, if it's unordered, use a set. If you need to count repeats...write a multiset class (maybe from collections.Counter)....anyway, you get the idea.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
since the str object offers isdigit(), isalpha(), isalphanum() functions, which all follow a well known python builtin pattern, I would create my own version for vowels, which is:
>>>isvowel = 'aeiouAEIOUàáâäǎæãåāaèéêëěẽēėęìíîïǐĩīıįòóôöǒœøõōùúûüǔũūűű'._contains_
From there, summing booleans is a trick. A violation of POLA (principle of least astonishment), so I agree, it's gotta go. We are NOT summing, we are COUNTING, so call a well known count method:
>>>return list(map(isvowel, text)).count(True)
That is tight, with zero "if" blocks and zero loops. Minimum cyclomatic complexity. No (implicit) casting bools to int, just counting True's in a list. Clear as day.
But, maybe too compact, so comment your code:
try
# create mapper to check is vowel
is_vowel_map = map(isvowel, text)
except TypeError as err:
raise [some message] from err
# check each character's vowel status
check_list = list(is_vowel_map)
# count isvowel == True in check_list
vowel_count = check_list.count(True)
return vowel_count
1
-
1
-
1
-
1
-
1
-
1
-
@spaghettiking653 I know I side stepped your point, but it's also meta: how would you type hint the I/O of iter()? I sent it a class method and a str, though in general use: the sentinel can be any object. The point: useful functions take a lot of types.
If I have a function that takes a specific object, I'd make it an object.method(). If it takes two specific objects, I'd think about coupling and cohesion. I don't know if I want to send it a different duck later.
IRL, I had a processor that looked at data from a satellite: it took rev number (orbits since launch)....an obvious int. But no, later, b/c bandwidth and available downlink stations, the rev nums could be split with an appended str....so the whole thing was a string, so in the code I just took str(arg) and dealt with it. Older code could still pass ints, but the new str cases were handled. I didn't care what was passed, as long as it had a __str__(), and if it failed then, I'd raise an InvalidRevNum(ValueError), which then tells users: you f*** up.
1
-
1
-
Here's my one liner for creating a cyclomatic polynomial class instance (in 2x):
cyclomatic = type('CyclotomicPolynomial', (object,),
{'__call__': lambda self, n, x: self[n](x),
'__getitem__’: lambda self, n:
scipy.poly1d(
map(
scipy.real,
reduce(
scipy.polymul,
[
scipy.poly1d(
[1,0]
)-
scipy.exp(1j*(2*scipy.pi/n))**k
for k in
filter(
lambda a:
(
2*sum(
[
(k*n//a) for k in range(1,a)
]
)+a+n-a*n)==1,
range(1,n+1)
)
]
)
)
)
}
)()
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I like that. Let's say each line comes from
header = 'yyyy mm dd gain(dB) pol"
looking like
line = '2023 07 28 104.5449 HV'
you just make
slicer = [slice(header.index(item), header.index(item) + len(item) for item in header.split()]
and
caster = [int] * 4 + [float, str]
and read your lines and cast the words all at once:
def iread(name):
with open(name, 'r') as fsrc:
for line in iter(fsrc.readline, ""):
yield [cast(value) for cast, value in zip(caster, [line.strip()[slice_] for slice_ in slicer]]
date = list(iread(name))
the point is, if the file format changes or gets another column, you just change static variables and not executables. Static complexity over dynamic complexity.
plus we got context management, zip, Sentinel Pattern use of iter(), slices, list comps, and a generator, and not an integer index in sight. classic python.
Question: can itertools.islice make this tighter?
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1