Skip to content

Commit d97e77d

Browse files
authored
Better Composition repr (#3182)
* include cls_name and oxidation states in Composition.__repr__ * delete doctest.testmod() if __name__ == "__main__" * add tests for Composition.__repr__ and __str__ * merge new tests into test_str_and_repr()
1 parent 10d5cc5 commit d97e77d

File tree

3 files changed

+26
-30
lines changed

3 files changed

+26
-30
lines changed

pymatgen/core/composition.py

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,7 @@ def __iter__(self) -> Iterator[Species | Element | DummySpecies]:
151151
def __contains__(self, key) -> bool:
152152
try:
153153
sp = get_el_sp(key)
154-
# First check for species
155-
if sp in self._data:
156-
return True
157-
# If not found, check for parent element (if it's a species)
158-
if isinstance(sp, Species):
159-
return sp.element in self._data
160-
return False
154+
return sp in self._data
161155
except ValueError as exc:
162156
raise TypeError(f"Invalid {key=} for Composition") from exc
163157

@@ -405,14 +399,14 @@ def get_integer_formula_and_factor(
405399
Li0.5O0.25 returns (Li2O, 0.25). O0.25 returns (O2, 0.125)
406400
"""
407401
el_amt = self.get_el_amt_dict()
408-
g = gcd_float(list(el_amt.values()), 1 / max_denominator)
402+
gcd = gcd_float(list(el_amt.values()), 1 / max_denominator)
409403

410-
d = {k: round(v / g) for k, v in el_amt.items()}
411-
formula, factor = reduce_formula(d, iupac_ordering=iupac_ordering)
404+
dct = {k: round(v / gcd) for k, v in el_amt.items()}
405+
formula, factor = reduce_formula(dct, iupac_ordering=iupac_ordering)
412406
if formula in Composition.special_formulas:
413407
formula = Composition.special_formulas[formula]
414408
factor /= 2
415-
return formula, factor * g
409+
return formula, factor * gcd
416410

417411
@property
418412
def reduced_formula(self) -> str:
@@ -434,8 +428,8 @@ def hill_formula(self) -> str:
434428
no carbon, all the elements, including hydrogen, are listed
435429
alphabetically.
436430
"""
437-
c = self.element_composition
438-
elements = sorted(el.symbol for el in c)
431+
elem_comp = self.element_composition
432+
elements = sorted(el.symbol for el in elem_comp)
439433
hill_elements = []
440434
if "C" in elements:
441435
hill_elements.append("C")
@@ -445,7 +439,7 @@ def hill_formula(self) -> str:
445439
elements.remove("H")
446440
hill_elements += elements
447441

448-
formula = [f"{el}{formula_double_format(c[el]) if c[el] != 1 else ''}" for el in hill_elements]
442+
formula = [f"{el}{formula_double_format(elem_comp[el]) if elem_comp[el] != 1 else ''}" for el in hill_elements]
449443
return " ".join(formula)
450444

451445
@property
@@ -621,8 +615,10 @@ def valid(self) -> bool:
621615
"""
622616
return not any(isinstance(el, DummySpecies) for el in self.elements)
623617

624-
def __repr__(self) -> str:
625-
return "Comp: " + self.formula
618+
def __repr__(self):
619+
formula = " ".join(f"{k}{':' if hasattr(k, 'oxi_state') else ''}{v:g}" for k, v in self.items())
620+
cls_name = type(self).__name__
621+
return f"{cls_name}({formula!r})"
626622

627623
@classmethod
628624
def from_dict(cls, d) -> Composition:
@@ -1299,9 +1295,3 @@ def __repr__(self):
12991295

13001296
class CompositionError(Exception):
13011297
"""Exception class for composition errors."""
1302-
1303-
1304-
if __name__ == "__main__":
1305-
import doctest
1306-
1307-
doctest.testmod()

pymatgen/core/tests/test_composition.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ def test_init(self):
8989
c = Composition({"S": Composition.amount_tolerance / 2})
9090
assert len(c.elements) == 0
9191

92+
def test_str_and_repr(self):
93+
test_cases = [
94+
({"Li+": 2, "Mn3+": 2, "O2-": 4}, {"str": "Li+2 Mn3+2 O2-4", "repr": "Composition('Li+:2 Mn3+:2 O2-:4')"}),
95+
("H2O", {"str": "H2 O1", "repr": "Composition('H2 O1')"}),
96+
({"Fe3+": 2, "O2-": 3}, {"str": "Fe3+2 O2-3", "repr": "Composition('Fe3+:2 O2-:3')"}),
97+
("C6H6", {"str": "C6 H6", "repr": "Composition('C6 H6')"}),
98+
]
99+
100+
for comp, expected in test_cases:
101+
assert str(Composition(comp)) == expected["str"]
102+
assert repr(Composition(comp)) == expected["repr"]
103+
92104
def test_average_electroneg(self):
93105
electro_negs = (2.7224999999999997, 2.4160000000000004, 2.5485714285714285, 2.21, 2.718, 3.08, 1.21, 2.43)
94106
for elem, val in zip(self.comps, electro_negs):

pymatgen/core/units.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -789,9 +789,9 @@ def get_mass():
789789
790790
"""
791791

792-
def wrap(f):
792+
def wrap(func):
793793
def wrapped_f(*args, **kwargs):
794-
val = f(*args, **kwargs)
794+
val = func(*args, **kwargs)
795795
unit_type = _UNAME2UTYPE[unit]
796796

797797
if isinstance(val, (FloatWithUnit, ArrayWithUnit)):
@@ -816,9 +816,3 @@ def wrapped_f(*args, **kwargs):
816816
return wrapped_f
817817

818818
return wrap
819-
820-
821-
if __name__ == "__main__":
822-
import doctest
823-
824-
doctest.testmod()

0 commit comments

Comments
 (0)