particle_input
- plasmapy.particles.decorators.particle_input(
- callable_: Callable[[...], Any] | None = None,
- *,
- require: str | Iterable[str] | None = None,
- any_of: str | Iterable[str] | None = None,
- exclude: str | Iterable[str] | None = None,
- allow_custom_particles: bool = True,
- allow_particle_lists: bool = True,
Convert particle-like arguments into particle objects.
When a callable is decorated with
particle_input()
, particle-like arguments that are appropriately annotated (i.e., withParticleLike
orParticleListLike
) will be converted into aParticle
,CustomParticle
, orParticleList
.The parameters
Z
andmass_numb
may be used to specify the charge number of an ion and mass number of an isotope, respectively, as long asZ
and/ormass_numb
are parameters of the callable and only one parameter is annotated withParticleLike
orParticleListLike
.To indicate that
None
can be passed to a parameter, annotate it withParticleLike | None
orParticleListLike | None
.If the particle representation does not satisfy any categorization criteria that have been provided, then
particle_input()
will raise an exception.If the annotated parameter is named
element
,isotope
, orion
, thenparticle_input()
will raise an exception if the argument provided to the callable is not consistent with parameter.Note
An annotated parameter named
ion
will accept neutral atoms andCustomParticle
-like objects as long as the charge number is explicitly defined. To enforce that the particle be charged, providerequire={"charged"}
toparticle_input()
.Note
When both
particle_input()
andvalidate_quantities()
are used to decorate a function, they may be used in either order. When using bothparticle_input()
andvalidate_quantities()
to decorate an instance method,particle_input()
should be the outer decorator andvalidate_quantities()
should be the inner decorator (see #2035).import astropy.units as u from plasmapy.particles import particle_input, ParticleLike from plasmapy.utils.decorators.validators import validate_quantities class SomeClass: @particle_input @validate_quantities def instance_method(self, particle: ParticleLike, B: u.Quantity[u.T]): ...
Note
When decorating a class method with
particle_input()
,classmethod
should be the outer decorator andparticle_input()
should be the inner decorator, and the first argument (representing the class) must be namedcls
.- Parameters:
callable (callable, optional) – The function or method to be decorated.
require (
str
|set
|list
|tuple
, keyword-only, optional) – Categories that each particle are required to be in.any_of (
str
|set
|list
|tuple
, keyword-only, optional) – Categories of which each particle must belong to at least one.exclude (
str
|set
|list
|tuple
, keyword-only, optional) – Categories that each particle cannot be in.allow_custom_particles (bool, keyword-only, default:
True
) – IfTrue
, allowCustomParticle
instances to be passed through.allow_particle_lists (bool, keyword-only, default:
True
) – IfTrue
, allowParticleList
instances to be passed through.
- Return type:
callable
- Raises:
TypeError – If the annotated argument is not particle-like; or if
Z
ormass_numb
is not an integer.InvalidParticleError – If the annotated argument does not correspond to a valid particle,
allow_custom_particles
isFalse
and the argument corresponds to aCustomParticle
, orallow_particle_lists
isFalse
and the argument corresponds to aParticleList
.InvalidParticleError – If the decorated argument has charge and/or mass number information, and
Z
and/ormass_numb
contain contradictory information.InvalidElementError – If an annotated argument is named
element
, and the input does not correspond to an element, isotope, or ion.InvalidIsotopeError – If an annotated argument is named
isotope
, and the input does not correspond to an isotope or an ion of an isotope.InvalidIonError – If an annotated argument is named
ion
, and the input does not correspond to an ion.ChargeError – If
"charged"
is in therequire
argument and the particle is not explicitly charged, or ifany_of = {"charged", "uncharged"}
and the particle does not have charge information associated with it.ParticleError – If the returned particle(s) do not meet the categorization criteria specified through
require
,any_of
, orexclude
; or if none of the parameters ofcallable_
have been appropriately annotated.UnitConversionError – If the annotated argument is a
Quantity
, but does not have a physical type of mass or charge.
- Warns:
ParticleWarning
– If decorated argument has charge and/or mass number information, andZ
and/ormass_numb
contain redundant information.
See also
Notes
There are some known limitations to
particle_input()
.Particle categorization criteria are not yet applied to arguments that get converted into a
ParticleList
(see #2048).This decorator is not compatible with setters (see #2507).
particle_input()
has limited compatibility with positional-only, variadic positional, and variadic keyword arguments (see #2150).When
particle_input()
andvalidate_quantities()
are both used to decorate an instance method on a class,particle_input()
must be the outside decorator (see #2035).Because it dynamically changes arguments, functions decorated with
particle_input()
often do not work well with static type checkers like mypy. These errors may be silenced by commenting# type: ignore[union-attr]
on a line of code, whereunion-attr
is the name of the mypy error code.
Examples
The
particle_input()
decorator takes appropriately annotated particle-like or particle-list-like arguments that are provided tocallable_
, and converts them into aParticle
,CustomParticle
, orParticleList
object.The following decorated function accepts a particle-like input and returns the corresponding particle object.
>>> from plasmapy.particles import particle_input, ParticleLike >>> import astropy.units as u
>>> @particle_input ... def get_particle(particle: ParticleLike): ... return particle
>>> get_particle("p+") Particle("p+") >>> get_particle(["p+", "e-"]) ParticleList(['p+', 'e-']) >>> get_particle(1e-26 * u.kg) CustomParticle(mass=1e-26 kg, charge=nan C)
To allow
None
to pass, useParticleLike | None
as the annotation.>>> from typing import Optional >>> @particle_input ... def get_particle_or_none(particle: ParticleLike | None): ... return particle >>> get_particle_or_none("p+") Particle("p+") >>> print(get_particle_or_none(None)) None
If the decorated callable has parameters named
Z
and/ormass_numb
and exactly one annotated parameter, thenZ
may be used to provide the charge number andmass_numb
may be used to provide the mass number. MakingZ
andmass_numb
keyword-only reduces the chances of confusion or mistakes.>>> @particle_input ... def make_particle(particle: ParticleLike, *, Z=None, mass_numb=None): ... return particle >>> make_particle("H", Z=0, mass_numb=3) Particle("T 0+")
Instance methods can also be decorated with
particle_input()
as long as the first argument (representing the instance itself) is namedself
following standard convention.>>> class SampleClass: ... @particle_input ... def __init__(self, particle: ParticleLike): ... self.particle = particle ... ... @particle_input ... def return_particle(self, new_particle: ParticleLike): ... return new_particle
>>> instance = SampleClass("α") >>> instance.particle Particle("He-4 2+") >>> instance.return_particle("T+") Particle("T 1+")
The
allow_custom_particles
andallow_particle_lists
keyword arguments indicate whethercallable_
should acceptCustomParticle
andParticleList
objects, respectively.>>> @particle_input(allow_custom_particles=False, allow_particle_lists=False) ... def get_atomic_number(particle: ParticleLike): ... return particle.atomic_number >>> get_atomic_number("Fe") 26
The
require
,any_of
, andexclude
keyword arguments may be used to specify categorization criteria that a particle must be consistent with. For more details, please refer to the docstring foris_category
.>>> @particle_input(require="element", any_of={"charged", "uncharged"}) ... def return_ionic_level(particle: ParticleLike): ... return particle >>> return_ionic_level("Fe-56 0+") Particle("Fe-56 0+")
When the parameter is named
element
,isotope
, orion
, then the corresponding argument must be consistent with the name. When the parameter is namedion
, then the particle(s) may also be a neutral atom as long as the charge number is explicitly defined.>>> @particle_input ... def mass_number(isotope: ParticleLike): ... return isotope.mass_number >>> mass_number("D") 2