from brian2 import check_units)
Decorator to check units of arguments passed to a function
In case the input arguments or the return value do not have the expected dimensions.
If an input argument or return value was expected to be a boolean but is not.
This decorator will destroy the signature of the original function, and replace it with the signature
(*args, **kwds). Other decorators will do the same thing, and this decorator critically needs to know the signature of the function it is acting on, so it is important that it is the first decorator to act on a function. It cannot be used in combination with another decorator that also needs to know the signature of the function.
Note that the
booltype is “strict”, i.e. it expects a proper boolean value and does not accept 0 or 1. This is not the case the other way round, declaring an argument or return value as “1” does allow for a
>>> from brian2.units import * >>> @check_units(I=amp, R=ohm, wibble=metre, result=volt) ... def getvoltage(I, R, **k): ... return I*R
You don’t have to check the units of every variable in the function, and you can define what the units should be for variables that aren’t explicitly named in the definition of the function. For example, the code above checks that the variable wibble should be a length, so writing
>>> getvoltage(1*amp, 1*ohm, wibble=1) Traceback (most recent call last): ... DimensionMismatchError: Function "getvoltage" variable "wibble" has wrong dimensions, dimensions were (1) (m)
>>> getvoltage(1*amp, 1*ohm, wibble=1*metre) 1. * volt
passes. String arguments or
Noneare not checked
>>> getvoltage(1*amp, 1*ohm, wibble='hello') 1. * volt
By using the special name
result, you can check the return value of the function.
You can also use
boolas a special value to check for a unitless number or a boolean value, respectively:
>>> @check_units(value=1, absolute=bool, result=bool) ... def is_high(value, absolute=False): ... if absolute: ... return abs(value) >= 5 ... else: ... return value >= 5
This will then again raise an error if the argument if not of the expected type:
>>> is_high(7) True >>> is_high(-7, True) True >>> is_high(3, 4) Traceback (most recent call last): ... TypeError: Function "is_high" expected a boolean value for argument "absolute" but got 4.
If the return unit depends on the unit of an argument, you can also pass a function that takes the units of all the arguments as its inputs (in the order specified in the function header):
>>> @check_units(result=lambda d: d**2) ... def square(value): ... return value**2
If several arguments take arbitrary units but they have to be consistent among each other, you can state the name of another argument as a string to state that it uses the same unit as that argument.
>>> @check_units(summand_1=None, summand_2='summand_1') ... def multiply_sum(multiplicand, summand_1, summand_2): ... "Calculates multiplicand*(summand_1 + summand_2)" ... return multiplicand*(summand_1 + summand_2) >>> multiply_sum(3, 4*mV, 5*mV) 27. * mvolt >>> multiply_sum(3*nA, 4*mV, 5*mV) 27. * pwatt >>> multiply_sum(3*nA, 4*mV, 5*nA) Traceback (most recent call last): ... brian2.units.fundamentalunits.DimensionMismatchError: Function 'multiply_sum' expected the same arguments for arguments 'summand_1', 'summand_2', but argument 'summand_1' has unit V, while argument 'summand_2' has unit A.