Obsah
1. Práce s jednotkami a veličinami: aby se nesčítaly hrušky s jablky
3. Základní práce s jednotkami
4. Operace s hodnotami s veličinami
5. Výpočty s různými veličinami
6. Nekorektní kombinace veličin
10. Explicitní zobrazení jednotek a veličin
11. Prefixy (předpony) jednotek
12. Binární prefixy, převody bajtů na bity
15. Převod na základní jednotky
16. Formátování hodnot s jednotkami
17. Příloha 1: výpis všech jednotek nabízených knihovnou Pint
18. Příloha 2: podpora pro práci s jednotkami v jazyku Kawa
19. Repositář s demonstračními příklady
1. Práce s jednotkami a veličinami: aby se nesčítaly hrušky s jablky
V mnoha aplikacích a informačních systémech se provádí jednodušší i složitější výpočty. A právě při vývoji algoritmů, které výpočty provádí, se používají hodnoty s různými jednotkami (metr, sekunda, Kč, …), které odpovídají různým veličinám (délka, čas, měna atd.). Nabízí se tedy otázka, jakým způsobem dokáže programovací jazyk zajistit, aby se „nesčítaly hrušky s jablky“, tj. aby se při výpočtech používaly proměnné a konstanty se správnou veličinou. Příkladem může být výpočet rychlosti ze dvou hodnot představujících odlišné veličiny: vzdálenosti a času.
Tento problém se mnohdy na úrovni programovacího jazyka vůbec neřeší, protože informace o použité jednotce nebývá součástí informace o datovém typu (hodnoty či proměnné – podle toho, zda je jazyk typovaný staticky či dynamicky). Jinými slovy pouze například víme, že proměnná obsahuje hodnotu typu float, ale již nevíme, zda tato hodnota představuje vzdálenost v metrech, čas v sekundách, čas v rocích, teplotu apod. Samozřejmě je možné v objektově orientovaných jazycích namísto běžných hodnot používat pouze objekty různých typů, to však většinou vyžaduje explicitně naprogramovat všechny možné kombinace operací (tedy že můžeme sčítat délky, že výsledkem podílu délky a času bude rychlost atd.).
Alternativní způsob, jak pracovat s hodnotami, ke kterým jsou nějakým způsobem přiřazeny jednotky, spočívá ve využití knihoven, které tuto funkcionalitu již poskytují. Předností těchto knihoven bývá to, že již obsahují mnoho předdefinovaných jednotek, pravidla pro operace s hodnotami s různými jednotkami (viz již výše zmíněný podíl vzdálenosti a času atd.) a taktéž většinou možnost převodu mezi jednotkami (z milimetrů na palce, ze sekund na hodiny, mezi různými variantami specifikace úhlů či teploty…). Pro programovací jazyk Python takových knihoven existuje větší množství. V dnešním článku si popíšeme základní vlastnosti knihovny nazvané Pint (což je mimochodem taktéž označení jednotky „pinta“ pro veličinu „objem“), ovšem v navazujících textech se seznámíme i s některými dalšími podobně koncipovanými knihovnami.
2. Instalace knihovny Pint
Instalace knihovny Pint je velmi snadná, protože tato knihovna je dostupná přes PyPi a navíc nevyžaduje žádné další závislosti. Instalaci tedy provedeme standardním nástrojem pip pro právě přihlášeného uživatele:
$ pip3 install --user pint
Ze zpráv vypisovaných v průběhu instalace knihovny Pint je patrné, že se skutečně neinstalují žádné další tranzitivní závislosti:
Collecting pint Downloading Pint-0.23-py3-none-any.whl (305 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 305.0/305.0 kB 2.1 MB/s eta 0:00:00 Requirement already satisfied: typing-extensions in /usr/local/lib/python3.11/site-packages (from pint) (4.8.0) Installing collected packages: pint Successfully installed pint-0.23
Otestování, že je možné knihovnu naimportovat:
$ python3 >>> import pint >>> help(pint) Help on package pint: NAME pint DESCRIPTION pint ~~~~ Pint is Python module/package to define, operate and manipulate **physical quantities**: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units. :copyright: 2016 by Pint Authors, see AUTHORS for more details. :license: BSD, see LICENSE for more details.
3. Základní práce s jednotkami
Podívejme se nyní na zcela základní možnosti, která nám knihovna Pint poskytuje. V prvním příkladu nadeklarujeme novou proměnnou nazvanou t1, která obsahuje hodnotu „10 sekund“. Nejde tedy o pouhé bezrozměrné číslo 10, ale k číslu je přiřazena i jednotka:
from pint import UnitRegistry ureg = UnitRegistry() t1 = 10 * ureg.second print(t1)
Výsledek je zobrazen ve formě hodnoty + jednotky:
10 second
Zobrazit si můžeme i příslušnou veličinu a to přečtením atributu dimensionality:
from pint import UnitRegistry ureg = UnitRegistry() t1 = 10 * ureg.second print(t1.dimensionality)
Opět se podívejme na výsledek:
[time]
4. Operace s hodnotami s veličinami
Samozřejmě je umožněno, aby se s hodnotami, ke kterým jsou přiřazeny veličiny, prováděly obvyklé operace. Vyzkoušejme si například operace prováděné s dvojicí časových údajů:
from pint import UnitRegistry ureg = UnitRegistry() t1 = 10 * ureg.second t2 = 1 * ureg.minute print(t1) print(t2) print(t1*t1) print(1/t1) print(t1+t2) print(t1-t2) print(t2/t1)
Výsledky:
10 second 1 minute 100 second ** 2 0.1 / second 70 second -50 second 0.1 minute / second
A opět je možné si u všech výsledků zobrazit příslušnou veličinu:
from pint import UnitRegistry ureg = UnitRegistry() t1 = 10 * ureg.second t2 = 1 * ureg.minute x = t1*t1 y = 1/t1 z = t1 + t2 w = t1/t2 print(x.dimensionality) print(y.dimensionality) print(z.dimensionality) print(w.dimensionality)
Nyní je již korektně uvedeno, že poslední hodnota je bezrozměrná:
[time] ** 2 1 / [time] [time] dimensionless
5. Výpočty s různými veličinami
Prozatím jsme výpočty prováděli pouze s dvojicí časových údajů nebo bezrozměrné hodnoty a časového údaje. Pochopitelně však můžeme ve výpočtu použít i rozdílné veličiny. Asi nejjednodušším případem je výpočet rychlosti na základě zadané vzdálenosti a času:
from pint import UnitRegistry ureg = UnitRegistry() s = 100 * ureg.meter t = 20 * ureg.second print(s/t)
Vypočtený výsledek bude mít korektní hodnotu i jednotku:
5.0 meter / second
A opět si můžeme nechat odvodit i veličinu výsledku:
from pint import UnitRegistry ureg = UnitRegistry() s = 100 * ureg.meter t = 20 * ureg.second v = s / t print(v.dimensionality)
Výsledek by měl v tomto případě vypadat následovně:
[length] / [time]
6. Nekorektní kombinace veličin
Mnohé kombinace veličin nejsou korektní – jednalo by se totiž o ono pověstné sčítání hrušek s jablky. Zkusme například sečíst 10 sekund s jedním metrem:
from pint import UnitRegistry ureg = UnitRegistry() a = 10 * ureg.second b = 1 * ureg.meter c = a + b print(a.dimensionality) print(b.dimensionality) print(c.dimensionality)
Tento skript skončí chybou, protože při sčítání je vyhozena výjimka typu DimensionalityError:
File "07_wrong_op.py", line 8, in <module> c = a + b File "/home/ptisnovs/.local/lib/python3.11/site-packages/pint/facets/plain/quantity.py", line 1188, in __add__ return self._add_sub(other, operator.add) File "/home/ptisnovs/.local/lib/python3.11/site-packages/pint/facets/plain/quantity.py", line 108, in wrapped return f(self, *args, **kwargs) File "/home/ptisnovs/.local/lib/python3.11/site-packages/pint/facets/plain/quantity.py", line 1090, in _add_sub raise DimensionalityError( pint.errors.DimensionalityError: Cannot convert from 'second' ([time]) to 'meter' ([length])
7. Konverze jednotek
Knihovna Pint podporuje i konverze jednotek. Tato operace se pochopitelně provádí pro jednotky stejné veličiny a realizuje se metodou nazvanou to, které se předá kýžená jednotka. Je to vlastně velmi jednoduché:
from pint import UnitRegistry ureg = UnitRegistry() t1 = 1800 * ureg.second t2 = t1.to(ureg.minute) t3 = t1.to(ureg.hour) print(t1, t1.dimensionality) print(t2, t2.dimensionality) print(t3, t3.dimensionality)
Výsledkem bude stejná hodnota, ovšem pokaždé reprezentovaná odlišnými jednotkami:
1800 second [time] 30.0 minute [time] 0.5 hour [time]
Nejvíce jednotek nalezneme u veličiny délka (a možná objem), takže si to vyzkoušejme:
from pint import UnitRegistry ureg = UnitRegistry() s1 = 1000 * ureg.meter s2 = s1.to(ureg.millimetre) s3 = s1.to(ureg.nautical_mile) s4 = s1.to(ureg.inch) s5 = s1.to(ureg.ly) print(s1, s1.dimensionality) print(s2, s2.dimensionality) print(s3, s3.dimensionality) print(s4, s4.dimensionality) print(s5, s5.dimensionality)
Výsledky:
1000000.0 millimeter [length] 0.5399568034557236 nautical_mile [length] 39370.07874015748 inch [length] 1.0570008340246153e-13 light_year [length]
8. Plocha a rychlost
V praxi se poměrně často setkáme s plošnou mírou, popř. s objemem. Hodnoty tohoto typu lze získat snadno, například vynásobením dvou délek (plocha) či tří délek (objem) atd. A knihovna Pint obsahuje podporu pro většinu plošných a objemových jednotek, včetně (u plochy) arů a hektarů či u objemu například oné pinty, která dala knihovně jméno. Tyto jednotky lze použít i pro převody tak, jak je to ukázáno v dalším demonstračním příkladu:
from pint import UnitRegistry ureg = UnitRegistry() s1 = 100 * ureg.meter a1 = s1*s1 a2 = a1.to(ureg.are) a3 = a1.to(ureg.hectare) print(s1, s1.dimensionality) print(a1, a1.dimensionality) print(a2, a2.dimensionality) print(a3, a3.dimensionality)
Po spuštění se nejdříve vypíše hodnota reprezentující délku a poté tři hodnoty reprezentující plochu, pokaždé ovšem popsanou odlišnou jednotkou:
100 meter [length] 10000 meter ** 2 [length] ** 2 100.0 are [length] ** 2 1.0 hectare [length] ** 2
Podobně lze pracovat s rychlostí. Tu je možné získat například podílem délky (přesněji řečeno vzdálenosti) a času. A opět jsou k dispozici různé jednotky rychlosti, včetně jejího vyjádření v uzlech (knot):
from pint import UnitRegistry ureg = UnitRegistry() s = 1 * ureg.mile t = 1 * ureg.hour v = s/t v2 = v.to(ureg.kilometer_per_hour) v3 = v.to(ureg.meter_per_second) v4 = v.to(ureg.knot) print(v) print(v2) print(v3) print(v4)
Výsledky – stejná rychlost, pokaždé ovšem vypsaná s využitím odlišné jednotky:
1.0 mile / hour 1.609344 kilometer_per_hour 0.44704000000000005 meter_per_second 0.868976241900648 knot
9. Zrychlení a síla
Podobně se můžeme setkat se zrychlením, které je typicky vyjádřeno v jednotce metry za sekundu2:
from pint import UnitRegistry ureg = UnitRegistry() t = 1 * ureg.second s = 100 * ureg.meter v = s/t a = v/t print(t) print(v) print(a)
Takto vypadají výsledky zobrazené po spuštění skriptu:
1 second 100.0 meter / second 100.0 meter / second ** 2
Při znalosti zrychlení (nějaké hmoty) a hmotnosti lze vypočítat sílu (z čehož si můžeme snadno odvodit i použitou jednotku). Výpočet může vypadat následovně:
from pint import UnitRegistry ureg = UnitRegistry() m = 1.5 * ureg.kilogram t = 1 * ureg.second s = 100 * ureg.meter v = s/t a = v/t f = m*a print(m) print(t) print(v) print(a) print(f)
Opět se podívejme na výsledky, které se vypíšou po spuštění tohoto demonstračního příkladu:
1.5 kilogram 1 second 100.0 meter / second 100.0 meter / second ** 2 150.0 kilogram * meter / second ** 2
Ovšem můžeme též zapsat sílu s využitím jednotky N (Newton) a vypočítat energii:
from pint import UnitRegistry ureg = UnitRegistry() f1 = 10 * ureg.newton f2 = 20 * ureg.newton f3 = f1 + f2 l = 100 * ureg.meter e = f3 * l print(f1) print(f2) print(f3) print(e)
S výsledky:
10 newton 20 newton 30 newton 3000 meter * newton
10. Explicitní zobrazení jednotek a veličin
U každé hodnoty lze přečíst jak její veličinu, tak i jednotku. K tomuto účelu slouží atributy nazvané units a dimensionality (ten již známe). Otestujme si tedy, jaké veličiny a jednotky získáme pro všechny hodnoty použité při výpočtu síly, která působí na těleso určité hmotnosti a tím ho urychluje:
from pint import UnitRegistry ureg = UnitRegistry() m = 1.5 * ureg.kilogram t = 1 * ureg.second s = 100 * ureg.meter v = s/t a = v/t f = m*a print(m.units, "\n", m.dimensionality, "\n") print(t.units, "\n", t.dimensionality, "\n") print(v.units, "\n", v.dimensionality, "\n") print(a.units, "\n", a.dimensionality, "\n") print(f.units, "\n", f.dimensionality, "\n")
Po spuštění tohoto skriptu získáme následující pětici jednotek a veličin:
kilogram [mass] second [time] meter / second [length] / [time] meter / second ** 2 [length] / [time] ** 2 kilogram * meter / second ** 2 [length] * [mass] / [time] ** 2
11. Prefixy (předpony) jednotek
U všech jednotek lze zapisovat i prefixy, resp. předpony, ať již se jedná o prefixy definované v SI (kilo-, mega-, mili-, mikro-) tak i například prefixy deci- či deca-. Podívejme se nejdříve na použití prefixů společně s jednotkou gram:
from pint import UnitRegistry ureg = UnitRegistry() m1 = 1.5 * ureg.gram m2 = 1.5 * ureg.kilogram m3 = 1.5 * ureg.milligram m4 = 1.5 * ureg.decigram m5 = 1.5 * ureg.decagram m6 = 1.5 * ureg.microgram print(m1) print(m2) print(m3) print(m4) print(m5) print(m6) print() print(m1.to(ureg.gram)) print(m2.to(ureg.gram)) print(m3.to(ureg.gram)) print(m4.to(ureg.gram)) print(m5.to(ureg.gram)) print(m6.to(ureg.gram))
Tento skript nejprve vypíše všechny váhy s původními jednotkami s prefixy a posléze stejnou hodnotu, ovšem převedenou na gramy:
1.5 gram 1.5 kilogram 1.5 milligram 1.5 decigram 1.5 decagram 1.5 microgram 1.5 gram 1500.0 gram 0.0015 gram 0.15000000000000002 gram 15.0 gram 1.5e-06 gram
Podobně je možné použít předpony „směrem dolů“, což si pro změnu ukážeme na časové jednotce sekunda (poznámka: přiznám se, že prefixy za femto- pro mě byly novinkou):
from pint import UnitRegistry ureg = UnitRegistry() t1 = 1 * ureg.second t2 = 1 * ureg.millisecond t3 = 1 * ureg.microsecond t4 = 1 * ureg.nanosecond t5 = 1 * ureg.picosecond t6 = 1 * ureg.femtosecond t7 = 1 * ureg.attosecond t8 = 1 * ureg.zeptosecond t9 = 1 * ureg.yoctosecond t10 = 1 * ureg.rontosecond t11 = 1 * ureg.quectosecond print(t1) print(t2) print(t3) print(t4) print(t5) print(t6) print(t7) print(t8) print(t9) print(t10) print(t11) print() print(t1.to(ureg.second)) print(t2.to(ureg.second)) print(t3.to(ureg.second)) print(t4.to(ureg.second)) print(t5.to(ureg.second)) print(t6.to(ureg.second)) print(t7.to(ureg.second)) print(t8.to(ureg.second)) print(t9.to(ureg.second)) print(t10.to(ureg.second)) print(t11.to(ureg.second))
Tento skript opět nejdříve vypíše původní hodnoty a posléze tyto hodnoty převede na sekundy:
1 second 1 millisecond 1 microsecond 1 nanosecond 1 picosecond 1 femtosecond 1 attosecond 1 zeptosecond 1 yoctosecond 1 rontosecond 1 quectosecond 1 second 0.001 second 1e-06 second 1e-09 second 1e-12 second 1e-15 second 1e-18 second 1e-21 second 1e-24 second 1e-27 second 1e-30 second
Následují SI předpony „směrem nahoru“ (opět se přiznám, že předpony uvedené po prefixu exa- pro mě byly novinkou):
from pint import UnitRegistry ureg = UnitRegistry() p1 = 1 * ureg.watt p2 = 1 * ureg.kilowatt p3 = 1 * ureg.megawatt p4 = 1 * ureg.gigawatt p5 = 1 * ureg.terawatt p6 = 1 * ureg.petawatt p7 = 1 * ureg.exawatt p8 = 1 * ureg.zettawatt p9 = 1 * ureg.yottawatt p10 = 1 * ureg.ronnawatt p11 = 1 * ureg.quettawatt print(p1) print(p2) print(p3) print(p4) print(p5) print(p6) print(p7) print(p8) print(p9) print(p10) print(p11) print() print(p1.to(ureg.watt)) print(p2.to(ureg.watt)) print(p3.to(ureg.watt)) print(p4.to(ureg.watt)) print(p5.to(ureg.watt)) print(p6.to(ureg.watt)) print(p7.to(ureg.watt)) print(p8.to(ureg.watt)) print(p9.to(ureg.watt)) print(p10.to(ureg.watt)) print(p11.to(ureg.watt))
Výsledky:
1 watt 1 kilowatt 1 megawatt 1 gigawatt 1 terawatt 1 petawatt 1 exawatt 1 zettawatt 1 yottawatt 1 ronnawatt 1 quettawatt 1 watt 1000.0 watt 1000000.0 watt 1000000000.0 watt 1000000000000.0 watt 1000000000000000.0 watt 1e+18 watt 1e+21 watt 1e+24 watt 1e+27 watt 1.0000000000000002e+30 watt
12. Binární prefixy, převody bajtů na bity
Kromě desítkových předpon popsaných v předchozí kapitole nalezneme v knihovně Pint i binární předpony. Minimálně první tři až čtyři z těchto předpon asi není zapotřebí čtenářům Roota dopodrobna představovat, takže si hned ukažme jejich použití:
from pint import UnitRegistry ureg = UnitRegistry() c1 = 1 * ureg.byte c2 = 1 * ureg.kibibyte c3 = 1 * ureg.mebibyte c4 = 1 * ureg.gibibyte c5 = 1 * ureg.tebibyte c6 = 1 * ureg.pebibyte c7 = 1 * ureg.exbibyte c8 = 1 * ureg.zebibyte c9 = 1 * ureg.yobibyte print(c1) print(c2) print(c3) print(c4) print(c5) print(c6) print(c7) print(c8) print(c9) print() print(c1.to(ureg.byte)) print(c2.to(ureg.byte)) print(c3.to(ureg.byte)) print(c4.to(ureg.byte)) print(c5.to(ureg.byte)) print(c6.to(ureg.byte)) print(c7.to(ureg.byte)) print(c8.to(ureg.byte)) print(c9.to(ureg.byte))
Ze zobrazených výsledků je patrné, že každá další předpona znamená zvýšení hodnoty 1024× a nikoli 1000×:
1 byte 1 kibibyte 1 mebibyte 1 gibibyte 1 tebibyte 1 pebibyte 1 exbibyte 1 zebibyte 1 yobibyte 1 byte 1024.0 byte 1048576.0 byte 1073741824.0 byte 1099511627776.0 byte 1125899906842624.0 byte 1.152921504606847e+18 byte 1.1805916207174113e+21 byte 1.2089258196146292e+24 byte
S binárními předponami jsou spojeny jak jednotky bajt, tak i a bit, takže si ještě pro úplnost ukažme převody míry informace reprezentované v bajtech na bity:
from pint import UnitRegistry ureg = UnitRegistry() c1 = 1 * ureg.byte c2 = 1 * ureg.kibibyte c3 = 1 * ureg.mebibyte c4 = 1 * ureg.gibibyte c5 = 1 * ureg.tebibyte c6 = 1 * ureg.pebibyte c7 = 1 * ureg.exbibyte c8 = 1 * ureg.zebibyte c9 = 1 * ureg.yobibyte print(c1) print(c2) print(c3) print(c4) print(c5) print(c6) print(c7) print(c8) print(c9) print() print(c1.to(ureg.bit)) print(c2.to(ureg.bit)) print(c3.to(ureg.bit)) print(c4.to(ureg.bit)) print(c5.to(ureg.bit)) print(c6.to(ureg.bit)) print(c7.to(ureg.bit)) print(c8.to(ureg.bit)) print(c9.to(ureg.bit))
A takto vypadají výsledky (oproti předchozímu skriptu jsou vynásobeny osmi):
1 byte 1 kibibyte 1 mebibyte 1 gibibyte 1 tebibyte 1 pebibyte 1 exbibyte 1 zebibyte 1 yobibyte 8 bit 8192 bit 8388608 bit 8589934592 bit 8796093022208 bit 9007199254740992 bit 9223372036854775808 bit 9444732965739290427392 bit 9671406556917033397649408 bit
13. Práce s úhly
Ještě si pro úplnost vyzkoušíme práci s úhly, protože i v této oblasti existuje vetší množství více či méně užitečných jednotek. Poměrně zajímavou jednotkou je „turn(s)“, přičemž jeden turn (jedno otočení) odpovídá 360°. A opět je pochopitelně možné všechny úhlové jednotky převádět například na stupně, radiány atd. tak, jak je ukázáno na dalším příkladu:
from pint import UnitRegistry ureg = UnitRegistry() a = 0.5 * ureg.turns print(a) print(a.to(ureg.degrees)) print(a.to(ureg.radians)) print(a.to(ureg.arcminute))
Výsledky pro vstupní úhel odpovídající polovině otáčky:
0.5 turn 180.0 degree 3.141592653589793 radian 10799.999999999998 arcminute
14. Redukce jednotek
Knihovna Pint podporuje i takzvanou redukci (zjednodušení) jednotek. Při některých výpočtech totiž například výsledek může mít jednotku odpovídající m3/hektar, což je vlastně podíl objemové a plošné veličiny. Tento podíl lze zkrátit na délkovou veličinu. A tuto redukci, resp. zjednodušení, do jisté míry zajišťuje metoda nazvaná to_reduced_units, i když z výsledků dalšího příkladu je patrné, že mnohem čitelnější výsledek získáme explicitním převodem na zvolenou délkovou jednotku (například na milimetry):
from pint import UnitRegistry ureg = UnitRegistry() l = 1.5 * ureg.meter s = l * l v = s * l d = v / (10 * ureg.hectare) print(l) print(s) print(v) print(d) print(d.to_reduced_units()) print(d.to(ureg.millimeter))
Tento skript po svém spuštění nejdříve vypíše délku, dále vypočtenou plochu, vypočtený objem a poté hodnotu d odpovídající výšce (hloubce?) vody, která naprší na jednotku plochy:
1.5 meter 2.25 meter ** 2 3.375 meter ** 3 0.3375 meter ** 3 / hectare 3.375e-07 hectare ** 0.5 0.03375 millimeter
15. Převod na základní jednotky
Předposlední vlastností knihovny Pint, se kterou se dnes seznámíme, je podpora převodu hodnoty s jakoukoli jednotkou na základní jednotku dané veličiny. Pokusme se například nepatrně upravit skript z předchozí kapitoly takovým způsobem, aby se výsledná výška (hloubka) hladiny vody převedla na základní délkovou (SI) jednotku, tedy na metry:
from pint import UnitRegistry ureg = UnitRegistry() l = 1.5 * ureg.meter s = l * l v = s * l d = v / (10 * ureg.hectare) print(l) print(s) print(v) print(d) print(d.to_base_units())
Na posledním řádku výpisu je patrné, že se tento převod skutečně provedl:
1.5 meter 2.25 meter ** 2 3.375 meter ** 3 0.3375 meter ** 3 / hectare 3.375000000000001e-05 meter
Podobně například můžeme převést rychlost vypočtenou v jednotce „světelný rok/rok“ (světelný rok je pochopitelně vzdálenost, ne čas) na hodnotu reprezentovanou v metrech za sekundu, což je pro tuto veličinu základní jednotka:
from pint import UnitRegistry ureg = UnitRegistry() s = 4.0 * ureg.ly t = 10000 * ureg.year v = s / t print(s) print(t) print(v) print(v.to_base_units())
Výsledek 0,0004 světelného roku za rok je převeden na metry za sekundu:
4.0 light_year 10000 year 0.0004 light_year / year 119916.9832 meter / second
A konečně se podívejme na převod hodnoty reprezentující energii na její základní jednotku:
from pint import UnitRegistry ureg = UnitRegistry() f1 = 10 * ureg.newton f2 = 20 * ureg.newton f3 = f1 + f2 l = 100 * ureg.meter e = f3 * l print(f1) print(f2) print(f3) print(e.to(ureg.joule)) print(e.to_base_units())
Takto vypadá výsledek:
10 newton 20 newton 30 newton 3000.0 joule 3000.0 kilogram * meter ** 2 / second ** 2
16. Formátování hodnot s jednotkami
Dostáváme se k poslední operaci, kterou si v dnešním článku popíšeme. Ukažme si, jak knihovna Pint podporuje naformátovaný výstup. Použijeme přitom hodnotu představující vypočtený tlak, přičemž veličinou je v tomto případě kg/m/s2. Formátovaný výstup bude proveden do HTML formátu a taktéž do formátu (La)TeXu:
from pint import UnitRegistry ureg = UnitRegistry() m = 1.5 * ureg.kilogram t = 1 * ureg.second s = 100 * ureg.meter v = s/t a = v/t f = m*a area = 10 * ureg.meter * ureg.meter pressure = f / area print(f) print(f"{f:H}") print(f"{f:L}") print(pressure) print(f"{pressure:H}") print(f"{pressure:L}")
Skript nejprve vypíše vypočtenou sílu (v plain textu, v HTML i v TeXu):
150.0 kilogram * meter / second ** 2 150.0 kilogram meter/second<sup>2</sup> 150.0\ \frac{\mathrm{kilogram} \cdot \mathrm{meter}}{\mathrm{second}^{2}}
Následně je vypsán vypočtený tlak:
15.0 kilogram / meter / second ** 2 15.0 kilogram/(meter second<sup>2</sup>) 15.0\ \frac{\mathrm{kilogram}}{\left(\mathrm{meter} \cdot \mathrm{second}^{2}\right)}
17. Příloha 1: výpis všech jednotek nabízených knihovnou Pint
Knihovna Pint má nadefinováno velké množství jednotek, které si můžeme snadno vypsat. Výpis všech jednotek do formy HTML tabulky zajišťuje následující krátký skript (který by možná bylo lepší přepsat s využitím filter a partition do více funkcionální podoby):
from pint import UnitRegistry ureg = UnitRegistry() print("<tr>", end="") c = 0 for item in dir(ureg): if item[0] != "_": print(f"<td>{item}</td>", end="") c += 1 if c == 6: print("</tr>\n<tr>", end="") c=0
Povšimněte si, že některé jednotky mohou být zapsány poměrně čitelně pomocí Unicode znaků:
% | A | A90 | A_US | A_it | Ah |
At | B | BDFT | BF | BTU | Ba |
Bd | Bi | Bq | Btu | Btu_iso | Btu_it |
Btu_th | C | C90 | Ci | Cl | Context |
D | DPI | Da | ECC | EC_therm | E_h |
Eh | F | FBM | F90 | Fr | G |
G0 | Gal | Gb | Group | Gy | H |
H2O | H90 | Hg | Hg_0C | Hg_32F | Hg_60F |
Hz | J | K | KPH | K_J | K_J90 |
K_alpha_Cu_d220 | K_alpha_Mo_d220 | K_alpha_W_d220 | L | Ly | M |
MPH | Measurement | Mx | N | N_A | Ne |
NeC | Nm | Np | Oe | P | PPCM |
PPI | PSH | Pa | Phi0 | Quantity | R |
RKM | R_K | R_K90 | R_inf | R_∞ | Rd |
Ry | S | SPL | St | Sv | System |
T | Ta | Td | Tj | Tt | U |
UK_bbl | UK_bushel | UK_cup | UK_cwt | UK_fluid_ounce | UK_force_ton |
UK_gallon | UK_gill | UK_horsepower | UK_hundredweight | UK_pint | UK_pk |
UK_quart | UK_ton | UK_ton_force | US_cwt | US_dry_barrel | US_dry_gallon |
US_dry_pint | US_dry_quart | US_fluid_dram | US_fluid_ounce | US_force_ton | US_hundredweight |
US_international_ampere | US_international_ohm | US_international_volt | US_liquid_cup | US_liquid_dram | US_liquid_fifth |
US_liquid_gallon | US_liquid_gill | US_liquid_ounce | US_liquid_quart | US_pint | US_shot |
US_therm | US_ton | US_ton_force | Unit | UnitsContainer | V |
VA | V90 | V_US | V_it | W | W90 |
Wb | Wh | Xu_Cu | Xu_Mo | Z0 | a |
a0 | a0 | a_u_action | a_u_current | a_u_electric_field | a_u_energy |
a_u_force | a_u_intensity | a_u_length | a_u_mass | a_u_temp | a_u_time |
abA | abC | abF | abH | abS | abV |
abampere | abcoulomb | aberdeen | abfarad | abhenry | abmho |
abohm | absiemens | abvolt | abΩ | acre | acre_feet |
acre_foot | add_context | alpha | amp | ampere | ampere_hour |
ampere_turn | amu | angstrom | angstrom_star | angular_degree | angular_minute |
angular_second | ap_dr | ap_lb | ap_oz | apothecary_dram | apothecary_ounce |
apothecary_pound | arc_minute | arc_second | arcdeg | arcdegree | arcmin |
arcminute | arcsec | arcsecond | are | astronomical_unit | at |
atm | atm_l | atmosphere | atmosphere_liter | atomic_mass_constant | atomic_unit_of_action |
atomic_unit_of_current | atomic_unit_of_electric_field | atomic_unit_of_energy | atomic_unit_of_force | atomic_unit_of_intensity | atomic_unit_of_length |
atomic_unit_of_mass | atomic_unit_of_temperature | atomic_unit_of_time | au | auto_reduce_dimensions | autoconvert_offset_to_baseunit |
avdp_dram | avdp_ounce | avdp_pound | avogadro_constant | avogadro_number | avoirdupois_dram |
avoirdupois_ounce | avoirdupois_pound | b | bag | bar | barad |
barie | barn | barrel | barrie | baryd | barye |
baud | bbl | becquerel | beer_barrel | beer_bbl | big_point |
biot | biot_turn | bit | bits_per_pixel | blob | board_feet |
board_foot | bohr | bohr_magneton | bohr_radius | boiler_horsepower | boltzmann_constant |
bp | bpp | bps | british_thermal_unit | bu | buckingham |
bushel | byte | c | c0 | c1 | c2 |
cables_length | cache_folder | cal | cal15 | cal_it | cal_th |
calorie | candela | candle | carat | case_sensitive | cc |
cd | celsius | centimeter | centimeter_H2O | centimeter_Hg | centimeter_Hg_0C |
centipoise | centuries | century | chain | characteristic_impedance_of_vacuum | check |
cicero | circle | circular_mil | classical_electron_radius | clausius | cmH2O |
cmHg | cm1 | cm_H2O | cm_Hg | cmil | common_year |
conductance_quantum | context | conventional_ampere90 | conventional_coulomb90 | conventional_farad90 | conventional_henry90 |
conventional_josephson_constant | conventional_mercury | conventional_ohm90 | conventional_volt90 | conventional_von_klitzing_constant | conventional_water |
conventional_watt90 | convert | cooling_tower_ton | coulomb | coulomb_constant | count |
counts_per_second | cp | cps | css_pixel | ct | cu_ft |
cu_in | cu_yd | cubic_centimeter | cubic_feet | cubic_foot | cubic_inch |
cubic_yard | cup | curie | cwt | cycle | d |
dB | dBm | dBu | d220 | dalton | darcy |
day | debye | decade | decibel | decibelmicrowatt | decibelmilliwatt |
decimeter | decitex | default_as_delta | default_format | default_system | define |
deg | degC | degF | degK | degR | degRe |
degree | degreeC | degreeF | degreeK | degreeR | degreeRe |
degree_Celsius | degree_Fahrenheit | degree_Kelvin | degree_Rankine | degree_Reaumur | degree_Réaumur |
delta_celsius | delta_degC | delta_degF | delta_degRe | delta_degreeC | delta_degreeF |
delta_degreeRe | delta_degree_Celsius | delta_degree_Fahrenheit | delta_degree_Reaumur | delta_degree_Réaumur | delta_fahrenheit |
delta_reaumur | delta_réaumur | den | denier | dgal | didot |
dirac_constant | disable_contexts | dot | dots_per_inch | dpi | dqt |
dr | drachm | dram | dry_barrel | dry_gallon | dry_pint |
dry_quart | dtex | dwt | dyn | dyne | e |
eV | electric_constant | electrical_horsepower | electron_g_factor | electron_mass | electron_volt |
elementary_charge | enable_contexts | entropy_unit | enzyme_unit | enzymeunit | eon |
eps0 | eps0 | epsilon0 | erg | esu | eu |
eulers_number | fahrenheit | farad | faraday | faraday_constant | fathom |
feet | feet_H2O | femtometer | fermi | fifteen_degree_calorie | fifth |
fine_structure_constant | first_radiation_constant | fldr | floz | fluid_dram | fluid_ounce |
fluidram | fm | fmt_locale | foot | foot_H2O | foot_per_second |
foot_pound | footpound | force_gram | force_kilogram | force_long_ton | force_metric_ton |
force_ndarray | force_ndarray_like | force_ounce | force_pound | force_short_ton | force_t |
force_ton | fortnight | fps | franklin | ft | ftH2O |
ft_lb | fur | furlong | g | g0 | g0 |
g_e | gn | gal | galileo | gallon | gamma |
gamma_mass | gauss | get_base_units | get_compatible_units | get_dimensionality | get_group |
get_name | get_root_units | get_symbol | get_system | gf | gi |
gilbert | gill | gon | gr | grad | grade |
grain | gram | gram_force | gravitational_constant | gravity | gray |
gregorian_year | h | ha | hand | hartree | hartree_energy |
hbar | hectare | henry | hertz | hogshead | horsepower |
hour | hp | hr | hundredweight | hydraulic_horsepower | impedance_of_free_space |
imperial_barrel | imperial_bbl | imperial_bu | imperial_bushel | imperial_cp | imperial_cup |
imperial_fldr | imperial_floz | imperial_fluid_drachm | imperial_fluid_dram | imperial_fluid_ounce | imperial_fluid_scruple |
imperial_gal | imperial_gallon | imperial_gi | imperial_gill | imperial_minim | imperial_peck |
imperial_pint | imperial_pk | imperial_pt | imperial_qt | imperial_quart | in |
inHg | in_Hg | inch | inch_H2O_39F | inch_H2O_60F | inch_Hg |
inch_Hg_32F | inch_Hg_60F | inches | international_british_thermal_unit | international_calorie | international_feet |
international_foot | international_inch | international_inches | international_knot | international_mile | international_steam_table_calorie |
international_yard | is_compatible_with | jig | josephson_constant | joule | julian_year |
jute | k | k_B | k_C | karat | kat |
katal | kayser | kelvin | kgf | kilogram | kilogram_force |
kilometer | kilometer_per_hour | kilometer_per_second | kip | kip_per_square_inch | knot |
knot_international | kph | kps | ksi | kt | l |
lambda | lambert | langley | lattice_spacing_of_Si | lb | lbf |
lbt | league | leap_year | li | light_year | lightyear |
link | liquid_cup | liquid_gallon | liquid_gill | liquid_pint | liquid_quart |
liter | litre | lm | ln10 | load_definitions | long_hundredweight |
long_ton | long_ton_force | lumen | lunar_month | lux | lx |
ly | m | m_e | mn | m_p | m_u |
magnetic_constant | magnetic_flux_quantum | mas | maxwell | mean_international_ampere | mean_international_ohm |
mean_international_volt | mercury | mercury_60F | meter | meter_per_second | metre |
metric_horsepower | metric_ton | metric_ton_force | mho | mi | microgram |
microliter | micrometer | micromole | micron | mil | mil_length |
mile | mile_per_hour | millennia | millennium | milliarcsecond | milligram |
millimeter | millimeter_Hg | millimeter_Hg_0C | min | minim | minute |
mmHg | mm_Hg | mol | molar | molar_gas_constant | mole |
molec | molecule | month | mph | mpl_formatter | mps |
mu0 | mu0 | mu_B | muN | nautical_mile | neper |
neutron_mass | newton | newtonian_constant_of_gravitation | nit | nmi | non_int_type |
nuclear_magneton | number_english | number_meter | oct | octave | octet |
oersted | ohm | ohm90 | ohm_US | ohm_it | oil_barrel |
oil_bbl | ounce | ounce_force | oz | ozf | ozt |
parse_expression | parse_pattern | parse_unit_name | parse_units | parsec | particle |
pascal | pc | pdl | peak_sun_hour | peck | pel |
pennyweight | percent | perch | pi | pi_theorem | pica |
picture_element | pint | pixel | pixels_per_centimeter | pixels_per_inch | pk |
planck_constant | planck_current | planck_length | planck_mass | planck_temperature | planck_time |
point | poise | pole | pond | pound | pound_force |
pound_force_per_square_inch | poundal | pp | ppi | ppm | preprocessors |
printers_dpi | printers_pica | printers_point | proton_mass | psi | pt |
px | qt | quad | quadrillion_Btu | quart | quarter |
r_e | rad | radian | rads | rankine | rd |
reaumur | reciprocal_centimeter | refrigeration_ton | rem | remove_context | revolution |
revolutions_per_minute | revolutions_per_second | reyn | rhe | rod | roentgen |
rpm | rps | rutherford | rydberg | rydberg_constant | réaumur |
röntgen | s | scaled_point | scruple | sec | second |
second_radiation_constant | section | separate_format_defaults | set_fmt_locale | setup_matplotlib | sft |
shake | short_hundredweight | short_ton | short_ton_force | shot | sidereal_day |
sidereal_month | sidereal_year | siemens | sievert | sigma | sigma_e |
slinch | slm | slpm | slug | slugette | smi |
sound_pressure_level | speed_of_light | sq_deg | sq_ft | sq_in | sq_mi |
sq_perch | sq_pole | sq_rod | sq_yd | sqdeg | square_degree |
square_feet | square_foot | square_inch | square_inches | square_league | square_mile |
square_rod | square_survey_mile | square_yard | sr | standard_atmosphere | standard_gravity |
standard_liter_per_minute | statA | statC | statF | statH | statT |
statV | statWb | statampere | statcoulomb | statfarad | stathenry |
statmho | statohm | stattesla | statvolt | statweber | statΩ |
stefan_boltzmann_constant | steradian | stere | stilb | stokes | stone |
super_feet | super_foot | superficial_feet | superficial_foot | survey_foot | survey_link |
survey_mile | sv | svedberg | sverdrup | synodic_month | sys |
t | tTNT | t_force | tablespoon | tansec | tbsp |
teaspoon | technical_atmosphere | tesla | tex | tex_cicero | tex_didot |
tex_pica | tex_point | tf | th | therm | thermochemical_british_thermal_unit |
thermochemical_calorie | thm | thomson_cross_section | thou | tlb | toe |
ton | ton_TNT | ton_force | ton_of_refrigeration | tonne | tonne_of_oil_equivalent |
torr | townsend | toz | tropical_month | tropical_year | troy_ounce |
troy_pound | tsp | turn | u | unified_atomic_mass_unit | unit_pole |
us_statute_mile | vacuum_permeability | vacuum_permittivity | volt | volt_ampere | von_klitzing_constant |
water | water_39F | water_4C | water_60F | watt | watt_hour |
watthour | weber | week | wien_frequency_displacement_law_constant | wien_u | wien_wavelength_displacement_law_constant |
wien_x | with_context | wraps | x_unit_Cu | x_unit_Mo | yard |
yd | year | yr | zeta | °C | °F |
°K | °R | °Re | µ | µ_0 | µ_B |
µ_N | Å | Å_star | ångström | ørsted | ħ |
Δcelsius | ΔdegC | ΔdegF | ΔdegRe | ΔdegreeC | ΔdegreeF |
ΔdegreeRe | Δdegree_Réaumur | Δfahrenheit | Δreaumur | Δréaumur | Δ°C |
Δ°F | Δ°Re | Φ_0 | Ω | Ω_90 | Ω_US |
Ω_it | α | γ | ε_0 | ζ | λ |
μ | π | σ | σ_e | ℎ | Å |
18. Příloha 2: podpora pro práci s jednotkami v jazyku Kawa
Python není prvním programovacím jazykem, do něhož byla přidána podpora pro práci s veličinami a jednotkami. Velmi pěkně je integrace jednotek řešena v jazyku Kawa, což je jedna z variant Scheme, která běží nad JVM a s níž jsme se již na stránkách Roota poměrně do podrobností seznámili (viz odkazy uvedené ve dvacáté kapitole). Mezi vymoženosti jazyka Kawa patří možnost přiřadit libovolné číselné hodnotě i její jednotku. Jednotka je k číselné hodnotě přiřazena v čase běhu aplikace a zúčastní se i operací prováděných s danou hodnotou (součet, součin, rozdíl, podíl). Můžeme tak například používat konstanty s přiřazenými jednotkami:
1A 0.5V 32cm 120s
Nebo též z běžné hodnoty:
(make-quantity 10 "cm") 10cm (make-quantity 10 unit:cm) 10cm
Pochopitelně lze vytvořit i proměnné a inicializovat je hodnotou i s uvedením jednotky. To se provádí následujícím způsobem:
(define MAX_CURRENT 1A) (define AVERAGE_DISTANCE 32cm) (define TIMEOUT 120s)
Je však možné použít libovolnou jednotku? Resp. přesněji řečeno, kontroluje se, zda je uvedená jednotka známá a nedopustíme se například omylem přepisu, jako v následujícím příkladu?
(define AVERAGE_DISTANCE 32cn)
Jednotky (jejich identifikátory) se skutečně kontrolují. Přímo v základních knihovnách programovacího jazyka Kawa jsou některé jednotky definovány, další je možné definovat podle toho, jaký konkrétní problém vyvíjená aplikace řeší. Definice nové jednotky může vypadat následovně:
(define-base-unit meter "distance") (define-unit stadion 176meter)
Vytvořili jsme jednotku „metr“ a „stadion“, který měří přibližně 176 metrů.
S novou jednotkou můžeme provést například součin s bezrozměrnou konstantou, což jednotku (ani veličinu) nijak nezmění:
(* 2stadion 3) 6.0stadion
To však není vše – jazyk Kawa totiž do určité míry dokáže odvodit i jednotku výsledku nějaké operace. U součtu a rozdílu je to jednoduché, ovšem podobně se odvozuje i jednotka při násobení a dělení:
(* 10cm 2) 20.0cm (* 10cm 10) 100.0cm (* 10cm 10cm) 100.0cm^2
Dělení hodnot s různými jednotkami:
(/ 10s 5cm) 2.0s*cm^-1 (/ 5cm 10s) 0.5cm*s^-1
(define-base-unit A "electric current") 3A 3.0A (define-base-unit V "voltage") 5V 5.0V (* 3A 5V) 15.0A*V (define power (* 3A 5V)) (define resistance (/ 5V 3A)) power 15.0A*V resistance 1.6666666666666667V*A^-1 (define wrong-power (* 3A 5cm)) wrong-power 15.0A*cm
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs:
20. Odkazy na Internetu
- Units and Quantities
https://uomresearchit.github.io/programming_with_python/06-units_and_quantities/index.html - Fyzikální veličina (Wikipedia)
https://cs.wikipedia.org/wiki/Fyzik%C3%A1ln%C3%AD_veli%C4%8Dina - Fyzikální jednotka (Wikipedia)
https://cs.wikipedia.org/wiki/Fyzik%C3%A1ln%C3%AD_jednotka - Soustava SI (Wikipedia)
https://cs.wikipedia.org/wiki/Soustava_SI - Dokumentace ke knihovně Pint
https://pint.readthedocs.io/en/stable/index.html - Knihovna astropy na PyPi
https://pypi.org/project/astropy/ - Knihovna Pint na PyPi
https://pypi.org/project/Pint/ - Repositář knihovny Pint
https://github.com/hgrecco/pint - Knihovna units na PyPi (nevyvíjený projekt)
https://pypi.org/project/units/ - Knihovna quantity na PyPi
https://pypi.org/project/quantity/ - Repositář s knihovnou quantity
https://github.com/mamrhein/quantity - Knihovna xarray-units na PyPi
https://pypi.org/project/xarray-units/ - Kawa: překvapivě silný a výkonný dialekt Scheme pro JVM
https://www.root.cz/clanky/kawa-prekvapive-silny-a-vykonny-dialekt-scheme-pro-jvm/ - Jazyk Kawa v ekosystému virtuálního stroje Javy
https://www.root.cz/clanky/jazyk-kawa-v-ekosystemu-virtualniho-stroje-javy/ - Zpracování vektorů, matic a N-rozměrných polí v programovacím jazyku Kawa
https://www.root.cz/clanky/zpracovani-vektoru-matic-a-n-rozmernych-poli-v-programovacim-jazyku-kawa/ - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/