Customizing the parser¶
Every piece of nameparser configuration sorts into one of three places
by asking what it varies with: vocabulary varies by language
(Lexicon), behavior varies by data source or
application (Policy), and presentation varies by
output destination (a rendering argument). See How the parser works for
why the split is drawn there.
Vocabulary: Lexicon¶
Adding and removing words¶
>>> from nameparser import Lexicon, Parser
>>> lex = Lexicon.default().add(titles={"dean"})
>>> Parser(lexicon=lex).parse("Dean Robert Johns").title
'Dean'
add() and remove()
both return a new Lexicon — the one you started
from (here, Lexicon.default()) is never mutated. Every field
accepts a plain set of lowercase words, keyword by field name (titles
above; particles, suffix_words, and the rest work the same
way) — see API reference for the full field list.
The default word lists themselves — TITLES, PARTICLES and the
other frozensets in nameparser.config — are frozen, so a runtime
addition belongs on a Lexicon as above, or on a
private Constants if you are still parsing through HumanName.
REGEXES and CAPITALIZATION_EXCEPTIONS are the two members the
freeze does not cover — they are still plain dicts. Editing one at
runtime is not a supported override, and it is not a clean no-op
either: the edit reaches a freshly built Constants, while the
shared CONSTANTS (copied at import) and the cached
default() never see it. That is the same
inconsistent reach the freeze removed for the word lists, so these
overrides belong on a config object too.
Five of these lists were renamed in 2.2 to match the field names used
here: PREFIXES, NON_FIRST_NAME_PREFIXES, BOUND_FIRST_NAMES,
FIRST_NAME_TITLES and SUFFIX_NOT_ACRONYMS became PARTICLES,
NON_GIVEN_NAME_PARTICLES, BOUND_GIVEN_NAMES,
GIVEN_NAME_TITLES and SUFFIX_WORDS, and two modules moved with
them. Names outside that list, TITLES among them, are unchanged.
Every 1.x name still imports, with a DeprecationWarning, until 3.0
— see Migrating from HumanName for the full mapping.
Vocabulary entries are matched one word at a time, with two
exceptions, so a multi-word entry like titles={"grand moff"} can
never match; the constructor warns when it sees one
(capitalization_exceptions keys included — they are looked up per
word too). The exceptions are given_name_titles, looked up as the
space-joined run of words already read as titles or as that run’s last
word — several titles written together are one form of address and the
last one does the addressing, so "Her Majesty Queen Elizabeth" is
read by queen — and
maiden_markers, matched by lookahead over the words as written:
maiden_markers={"z domu"} matches the pair and neither word alone,
which is how the shipped Polish entry works. The words have to stand
together — a bracketed clause or a comma between them ends the run, and
the first word is then an ordinary name word. Where a phrase entry and a
word entry starting with it are both configured, the phrase wins where
it matches and the word matches everywhere else. No warning is raised
for a multi-word entry in either of these two fields, since there it is
not a mistake.
The limit is on storage, not on the shape a name can have. Adjacent suffix words are reassembled after they match, so a multi-word credential is reachable as its component words even though the phrase itself cannot be stored:
>>> from nameparser import parse
>>> parse("John Smith, MD PhD").suffix
'MD PhD'
That has held since 1.4.0. A credential whose words are not in the default vocabulary is reached by adding those words, not the phrase:
>>> lex = Lexicon.default().add(suffix_acronyms={"leed", "ap"})
>>> Parser(lexicon=lex).parse("John Smith, LEED AP").suffix
'LEED AP'
Removing works the same way, and drops the word from recognition:
>>> lean = Lexicon.default().remove(titles={"professor"})
>>> Parser(lexicon=lean).parse("Professor Robert Johns").title
''
A few fields mark a subset of another — given_name_titles over
titles, particles_ambiguous over particles,
suffix_acronyms_ambiguous over suffix_acronyms,
conjunctions_ambiguous over conjunctions, and
honorific_tails over suffix_words. Entries belong in the base
field too, so add to both and remove from the marker first. Three of
them enforce that — particles_ambiguous, suffix_acronyms_ambiguous
and honorific_tails raise ValueError naming the orphans, because
an orphan in each of those does real harm rather than nothing. The
other two are deliberately unchecked because an orphan there is inert:
given_name_titles matches a title run as one space-joined string, or
by that run’s last word, so a legitimate entry like "sir and dame"
is no single word in titles; and a conjunctions_ambiguous entry
is only ever read for a word that is a conjunction, so
remove(conjunctions={"e"}) simply works and the stale marker entry
is never consulted.
Turning title detection off¶
The subset rule matters most when clearing a field wholesale. Emptying
titles alone orphans every given_name_titles entry, so the two
go together:
>>> d = Lexicon.default()
>>> lean = d.remove(titles=set(d.titles),
... given_name_titles=set(d.given_name_titles))
>>> Parser(lexicon=lean).parse("Hon Solo").given
'Hon'
Emptying the vocabulary does not switch titles off entirely, though. A
word ending in a period, standing at the front of the part that carries
the given name, is read as a title structurally, without consulting
titles at all — that is what lets unfamiliar ranks and
abbreviations work (see Titles you didn’t configure):
>>> bare = Parser(lexicon=Lexicon.empty())
>>> bare.parse("Professor John Smith").title # vocabulary gone
''
>>> bare.parse("Dr. John Smith").title # structural, stays
'Dr.'
Combining two lexicons¶
Whole lexicons compose with |, which unions field by field — handy
for keeping a shared house vocabulary separate from a per-source one
and combining them at parser construction:
>>> house = Lexicon.empty().add(titles={"dean"})
>>> per_source = Lexicon.empty().add(titles={"provost"})
>>> sorted((house | per_source).titles)
['dean', 'provost']
Fixing the case of a particular word¶
capitalization_exceptions is the one pair-valued field — each entry
maps a lowercase key to its exact-cased replacement ("phd" →
"PhD"), so it isn’t a fit for add()/remove(). Change it with
dataclasses.replace() instead, and pass the result to
capitalized():
>>> import dataclasses
>>> from nameparser import parse
>>> str(parse("jane smith dds").capitalized())
'Jane Smith Dds'
>>> default = Lexicon.default()
>>> lex = dataclasses.replace(
... default,
... capitalization_exceptions=tuple(default.capitalization_exceptions)
... + (("dds", "DDS"),))
>>> str(parse("jane smith dds").capitalized(lex))
'Jane Smith DDS'
Note the tuple(...) + ...: assigning a bare (("dds", "DDS"),)
would replace the default exceptions rather than extend them, so
"phd" and the rest would stop being fixed.
The key is matched against the token with punctuation normalized away,
not against the raw text, so one "phd" entry covers "phd",
"Phd", and "Ph.D." alike — you don’t need a separate key for
each way a source might punctuate it.
Words that are also ordinary names¶
Three fields — suffix_acronyms_ambiguous, particles_ambiguous
and conjunctions_ambiguous — mark entries from suffix_acronyms,
particles and conjunctions that are also plausible as ordinary
name words on their own (an acronym suffix that doubles as a nickname,
a particle that doubles as a given name, a connective letter that
doubles as an initial). They don’t add new vocabulary by themselves;
they narrow how an existing
entry is read when it appears alone. If you’re not sure whether a word
you’re adding is one of these ambiguous cases, weigh how often it is a
name against how often it is the credential. Marking it ambiguous is
not free in either direction: an ambiguous acronym counts as a suffix
only when written with its periods, so the bare spelling reads as a
name and the parse reports the fork, and the comma form moves with it
— Smith, BA reads first BA rather than suffix BA, and a
bracketed John Smith (BA) falls through to nickname parsing. A
wrong unambiguous claim takes the credential reading silently and can
lose a real person’s surname. For particles_ambiguous the default
runs the other way: a particle that is not borne as a given name
belongs in the never-given half, which is where mc and ste
were moved (#360). The other
direction is to leave the word out of suffix_acronyms altogether,
which is the right answer when the name reading is the far more common
one: an acronym whose credential is tenuous or specialized beside a
common surname earns removal rather than a marking, and a caller who
needs it adds it back
with Lexicon.default().add(suffix_acronyms={"cha"}). That is what
the default vocabulary did with rai and cha in 2.3. (The same
conservatism is why dean above isn’t in the default vocabulary in
the first place: “Dean” is also a common given name, and a default
that swallowed it as a title would misparse “Dean Martin” for
everyone.)
ma is a shipped example. It is both a credential and a common
surname, so it is listed in suffix_acronyms_ambiguous and counts as
a suffix only when written with periods:
>>> parse("Jack Ma").family
'Ma'
>>> parse("Jack M.A.").suffix
'M.A.'
particles_ambiguous is the same idea for surname particles. A
particle listed there may also be a given name, which is what makes a
leading one a decision to take; a particle not listed there never
is, so there is nothing to decide. That shows up in what a particle
standing alone at the front of a name does: a listed one is a name
part in its own right, while an unlisted one pulls the rest of the
name into the surname and leaves no given name at all. Which field a
listed particle lands in is name_order’s question, covered
below; an unlisted one opening the name is the surname under every
order, because a word that can never be a given name leaves the order
nothing to decide.
>>> parse("van Gogh").given # 'van' may be a given name
'van'
>>> parse("de Mesnil").given # 'de' may not
''
>>> parse("de Mesnil").family
'de Mesnil'
A comma forestalls the question rather than answering it. Writing the surname before the comma has already said which words are the surname, so a particle at the front of them decides nothing, and whatever follows the comma is the given name as usual:
>>> parse("de Mesnil, Juan").given # the comma named the surname
'Juan'
If your data never uses Van as a given name, take it out of the
ambiguous set: a leading van is then no decision at all, so no
ambiguity is recorded and it becomes part of the surname — under any
name_order, since that is what taking the word out asserted:
>>> lex = Lexicon.default().remove(particles_ambiguous={"van"})
>>> Parser(lexicon=lex).parse("van Gogh").family
'van Gogh'
conjunctions_ambiguous is the same idea for one-letter connectives.
A single letter written against the name’s own case is an initial and
one written with it is the connective — but a name written wholly in
one case, all upper or all lower, says nothing either way, and this is
the set that decides it there. e is the one entry shipped: a bare
E initial is common where an e between two surnames is rare, and
y runs the other way, so y joins even written as a bare capital.
>>> parse("jose e maria santos").middle # 'e' reads as an initial
'e maria'
>>> parse("JUAN GARCIA Y LOPEZ").family # 'y' joins
'GARCIA Y LOPEZ'
>>> parse("Jose e Maria Santos").given # mixed case decides itself
'Jose e Maria'
A member also reports the fork, so a caller can see which reading was taken:
>>> [a.kind for a in parse("jose e maria santos").ambiguities]
[<AmbiguityKind.CONJUNCTION_OR_INITIAL: 'conjunction-or-initial'>]
If your data is Portuguese, where e links surnames the way y
does in Spanish, take it out and the connective reading comes back:
>>> lex = Lexicon.default().remove(conjunctions_ambiguous={"e"})
>>> Parser(lexicon=lex).parse("jose e maria santos").given
'jose e maria'
If your data is Dutch, where a bare single letter is an initial and never a connective, add the other one instead:
>>> lex = Lexicon.default().add(conjunctions_ambiguous={"y"})
>>> Parser(lexicon=lex).parse("juan garcia y lopez").middle
'garcia y'
Bound given names¶
bound_given_names holds given-name prefixes that attach to the
following word to form one given name — abdul, abu, umm and
their Arabic-script spellings (عبد, أبو, أم) among them:
>>> parse("abdul salam ahmed salem").given
'abdul salam'
Add your own, or empty the set to switch the behavior off entirely:
>>> lex = Lexicon.default().add(bound_given_names={"mohamad"})
>>> Parser(lexicon=lex).parse("mohamad salam ahmed salem").given
'mohamad salam'
>>> d = Lexicon.default()
>>> off = d.remove(bound_given_names=set(d.bound_given_names))
>>> Parser(lexicon=off).parse("abdul salam ahmed salem").given
'abdul'
Behavior: Policy¶
When your data source or application needs different parsing behavior
— a different name order, stricter suffix rules, extra delimiters —
set it on Policy, a small, closed set of fields,
listed below.
Field |
Type |
Effect |
|---|---|---|
|
one of the three exported order constants |
Assigns positional (no-comma) input to given/middle/family in
this order. Use the exported |
|
|
Reorders patronymic-shaped names via opt-in detectors — East
Slavic formal order ( |
|
|
Folds |
|
|
Routes content enclosed by these delimiter pairs to
|
|
|
Routes content enclosed by these delimiter pairs to |
|
|
Adds separators that split suffix groups, e.g. |
|
|
Reads an initial-shaped suffix word after a comma as a suffix:
|
|
|
Excludes emoji from tokenization — they appear in no field or
rendered view, though |
|
|
Excludes bidirectional control characters the same way.
Defaults to |
To apply a PolicyPatch directly –
without going through a locale pack – call Policy.patched():
>>> from nameparser import Policy, PolicyPatch
>>> Policy().patched(PolicyPatch(middle_as_family=True))
Policy(middle_as_family=True)
Family-first name order¶
name_order is the one most likely to matter for data that is not
in Western order. Positional input is assigned in the order you
declare — with the two vocabulary exceptions noted at the end of this
section — so a name written family-first — Hungarian, here — parses as
written instead of needing to be rearranged afterwards:
>>> from nameparser import Parser, Policy, FAMILY_FIRST, parse
>>> parse("Nagy Laszlo Peter").family # default GIVEN_FIRST
'Peter'
>>> family_first = Parser(policy=Policy(name_order=FAMILY_FIRST))
>>> name = family_first.parse("Nagy Laszlo Peter")
>>> name.family, name.given, name.middle
('Nagy', 'Laszlo', 'Peter')
An explicit comma still wins, on the reasoning that someone who wrote
one meant it — so the same parser reads "Thomas, John" as
family-then-given regardless of the configured order:
>>> family_first.parse("Thomas, John").family
'Thomas'
A Vietnamese full name needs a third order. It is written family, then
middle, then given — the name a person is actually called by is the
last word, not the second. Family-first order gets the family name
right and then reverses the remaining two, so
FAMILY_FIRST_GIVEN_LAST exists for the names that read this way:
>>> from nameparser import FAMILY_FIRST_GIVEN_LAST
>>> family_first.parse("Tran Quoc Toan").given # FAMILY_FIRST
'Quoc'
>>> given_last = Parser(policy=Policy(name_order=FAMILY_FIRST_GIVEN_LAST))
>>> viet = given_last.parse("Tran Quoc Toan")
>>> viet.family, viet.middle, viet.given
('Tran', 'Quoc', 'Toan')
Nothing keys this order to a script the way the East Asian defaults
below do — Vietnamese is written in the Latin alphabet, which carries
no order of its own — so it applies only where you set it, and there
is no vn locale pack yet (issue #146).
Declaring the order settles where a surname ends¶
A surname particle joins forward, onto the word after it. Where a particle ends the name there is nothing ahead of it to join, and what it belongs to is decided by what the writing says rather than by the word. Two things say it, and both amount to someone stating that the family name came first — a family comma, and a declared family-first order:
>>> parse("Jong, Anke de").family # the comma says so
'de Jong'
>>> family_first.parse("Jong Anke de").family # the order says so
'de Jong'
That pair is not a coincidence but one shape written two ways: form 4
(Title Family Given Middle Middle [Particle] [, Suffix]) is form 2
(Family [Suffix], Title Given (Nickname) Middle Middle[,] Suffix [,
Suffix]) with the comma removed and the family folded inline —
titles included, so a title that form 2 writes after the comma leads
the name in form 4 instead. If your records are family-first without
commas, Policy(name_order=FAMILY_FIRST) reads them the way the
comma format is already read. Measured over the whole particle
vocabulary — every particle nameparser ships, crossed with three
families and three given names, 630 generated pairs in all — 603 of
630 agree (2026-08-30); the executable form of this correspondence is
tests/v2/test_order_correspondence.py.
Three limits keep that statement honest.
The correspondence covers one shape written two ways, not
comma-deletion in general: a name whose shape changes when the comma
is removed — a title or suffix crossing to a different position —
parses as the shape it becomes, not as a disagreeing reading of form
2. Where the trailing word is both particle and suffix vocabulary,
the two writings read it differently, and it is that ASYMMETRY rather
than a precedence that breaks the correspondence. The particle
attachment outranks the suffix reading on the comma side alone —
that is the scope the rule is stated in — so
parse("Ménil, Christophe vd") reads family vd Ménil, while
family_first.parse("Ménil Christophe vd") reads family Ménil
and suffix vd. A listing ending in one of those three words
therefore does not correspond between the two writings — this is the
whole of the 27 disagreeing pairs, not scatter. And FAMILY_FIRST is the only order the correspondence
reaches at all, because it is the only one that puts a trailing piece
in the middle, where a particle means nothing; FAMILY_FIRST_GIVEN_LAST
puts it in the given slot, where your own declaration already says
it is the given name, and no comma format writes the given name
last, so form 5 has no comma twin to correspond to in the first
place:
>>> given_last.parse("Nguyen Thi Van").given
'Van'
The declaration also bounds how far a leading particle run reaches. With no order declared, nothing marks where the surname ends and a particle followed by several words really can be all surname, so the whole name is read as one. Declaring family-first asserts that what follows the family is not more surname, which settles it:
>>> parse("de Mesnil Jean").family # nothing says where it ends
'de Mesnil Jean'
>>> family_first.parse("de Mesnil Jean").family # the order does
'de Mesnil'
>>> family_first.parse("de Mesnil Jean").given
'Jean'
In the default order, write the comma for that reading. The run stops
after one name word rather than one token, so it cannot cut inside a
conjunction-joined run or a bound given-name pair —
"de la Vega y Santos Juan" keeps family de la Vega y Santos.
Where two or more words are left over, the two family-first orders
differ from each other:
>>> family_first.parse("de la Cruz Juan Carlos").middle
'Carlos'
>>> given_last.parse("de la Cruz Juan Carlos").middle
'Juan'
Two cautions, both places where the vocabulary layer answers before
name_order is consulted at all.
The first is why the example above is not the more obvious
"Nguyen Van Minh": a middle word that is also a shipped particle
is claimed by the vocabulary layer. Van is the Dutch particle
van, so that name reads family Nguyen with Van Minh given
under both family-first orders, and the choice between them makes no
difference.
The second is at the front of a name, and there the vocabulary
overrides the declared order outright: where a particle that can never
be a given name stands alone as the opening piece, the whole name is
the surname, in every name_order. "de Mesnil" is family de
Mesnil under both family-first orders exactly as it is by default,
not family de with Mesnil given — a word that can never be a
given name leaves the order nothing to decide. Only the never-given
set does this: "van Gogh" reads family van, given Gogh
under a family-first order, because van can be a given name and
so leaves a real question to answer.
Words that are also ordinary names covers dropping a word from a vocabulary, or moving one between those two sets.
East Asian defaults, and turning them off¶
Two defaults key on the script a name is written in rather than on
anything you set: a name written wholly in Han or Hangul — or one
mixing kanji with kana — is assigned family-first (script_orders),
and an unspaced hangul name is split into surname and given name
against the shipped Korean census list (segment_scripts).
East Asian names explains the naming conventions both rest on —
this section is how to switch them off, which you can do separately:
>>> parse("김민준").family # both defaults on
'김'
>>> positional = Parser(policy=Policy(script_orders=()))
>>> positional.parse("김민준").family # still split
'민준'
>>> unsplit = Parser(policy=Policy(segment_scripts=frozenset()))
>>> unsplit.parse("김민준").family # one token, not split
'김민준'
The two switches interact, and clearing only script_orders
produces a third behavior rather than the old one: the split still
runs, so 김민준 still becomes two tokens, and the positional
default then assigns them given-first — the surname lands in
given. To restore nameparser 2.0’s reading exactly, clear both
fields.
To teach the splitter a surname it doesn’t ship with, add it to the
surnames vocabulary like any other word:
>>> lex = Lexicon.default().add(surnames={"김민"})
>>> Parser(lexicon=lex).parse("김민준").family
'김민'
Chinese surnames are deliberately absent from that default set,
because splitting Han text requires knowing Chinese from Japanese;
Locale packs covers the opt-in zh pack that supplies them.
The Japanese behaviors ride these same two fields, so they need no
switches of their own: script_orders=() clears the kana-licensed
entry along with the Han and Hangul ones, and
segment_scripts=frozenset()
deactivates every script at once, which also stops a parser consulting
whatever segmenter it was given. The segmenter has an off-switch as
well — Parser(segmenter=None), which is the default; see
Segmenters for what one is expected to do with text it
does not handle. Two behaviors are not policy fields at all, and apply
however these two fields are set. The katakana middle dot ・ separates
tokens the way a space does, decided in tokenization. And a listed CJK
honorific glued to the end of a name token is split off it — 田中さん
reads family 田中 with さん in suffix — because the tail
vocabulary carries its own license rather than borrowing a script’s:
every entry is a word that can never end a name, so there is no
per-script trust question for segment_scripts to answer. That
vocabulary is also the peel’s off-switch —
Lexicon.default().remove(honorific_tails={"さん"}) leaves
田中さん unsplit while the spaced 田中 さん still reads さん
as a suffix. Dropping a word from a marker field alone orphans
nothing, so that one needs no matching suffix_words edit. Emptying
the field is also the way to opt out of what the peel costs a
non-ASCII parse — an empty honorific_tails stops it at its first
guard, whereas segment_scripts never gated it and so cannot turn it
off.
Note
Every field here is annotated with its canonical storage type
rather than with everything the constructor accepts — the same as
capitalization_exceptions, and for the same reason: the
annotation is what you get back when you READ the attribute, which
is the commoner operation.
The constructor is deliberately wider. It takes any mapping for
script_orders, any iterable of Script for
segment_scripts, and plain strings wherever a Role is
wanted (Role is a StrEnum precisely so that works). A
dataclass cannot express those two types separately, so the
examples in this guide use the spellings that check clean under
mypy — () and frozenset(...) rather than {} and a bare
set literal. The wider spellings parse identically; they just need
a # type: ignore[arg-type] if you run a type checker.
Nicknames, maiden names, and brackets¶
A delimiter pair carries no meaning of its own, so what a clause reads
as is settled in steps. Suffix-shaped content is taken first: the
brackets are dropped and what was inside parses as if it had been
written bare, which is not the same as the clause becoming the suffix
("Jane Smith (née Jr.)" gives family née, suffix Jr.).
Then the content is asked whether it announces itself: a
clause opening with a recognized maiden marker and carrying a word
after it is a maiden name whatever encloses it, and needs nothing
configured. Only for what is left — markerless content, and a lone
marker word — does the PAIR decide, and that is what this knob is for.
Listing a pair here drops it from the effective
nickname_delimiters set automatically, and the one-liner is the
whole recipe:
>>> policy = Policy(maiden_delimiters=frozenset({("(", ")")}))
>>> Parser(policy=policy).parse("Jane (Jones) Smith").maiden
'Jones'
To add a delimiter pair rather than reroute one, build on the
exported default — assigning a bare set replaces the built-in pairs
instead of extending them, the same trap as capitalization_exceptions:
>>> from nameparser import DEFAULT_NICKNAME_DELIMITERS
>>> parse("Benjamin {Ben} Franklin").middle # not a pair by default
'{Ben}'
>>> policy = Policy(
... nickname_delimiters=DEFAULT_NICKNAME_DELIMITERS | {("{", "}")})
>>> Parser(policy=policy).parse("Benjamin {Ben} Franklin").nickname
'Ben'
Suffixes not separated by commas¶
extra_suffix_delimiters handles sources that separate post-nominals
with something other than a comma. The default reading of such a name
is bad enough to be the reason you’d go looking:
>>> name = parse("Jane Smith, RN - CRNA")
>>> name.given, name.family, name.suffix
('RN', 'Jane Smith', 'CRNA')
>>> policy = Policy(extra_suffix_delimiters={" - "})
>>> name = Parser(policy=policy).parse("Jane Smith, RN - CRNA")
>>> name.given, name.family, name.suffix
('Jane', 'Smith', 'RN, CRNA')
Keeping emoji and control characters¶
The strip flags keep characters the parser removes by default. Note what happens to an emoji you keep — it becomes a token like any other, and lands in the middle name:
>>> str(parse("Sam 😊 Smith")) # stripped by default
'Sam Smith'
>>> kept = Parser(policy=Policy(strip_emoji=False)).parse("Sam 😊 Smith")
>>> str(kept), kept.middle
('Sam 😊 Smith', '😊')
strip_bidi=False does the same for invisible bidirectional control
characters, which is occasionally what you want when round-tripping
right-to-left text verbatim.
Presentation: rendering arguments¶
Once a name is parsed, how it’s displayed is a separate decision made
at the point of output, not baked into the parse. Three methods on
ParsedName cover it — see API reference for full
signatures:
render()fills a format spec from the seven role fields.initials()is the same idea narrowed to first letters, with its owndelimiter/separatorarguments.capitalized()returns a new, case-fixedParsedNameinstead of a string. It only touches input that’s already single-case (all lower, all upper) unless you passforce=True— mixed case is left alone by default on the assumption that someone already capitalized it on purpose.
>>> from nameparser import parse
>>> name = parse("Dr. Juan Q. Xavier de la Vega III")
>>> name.render("{family}, {given} {middle}")
'de la Vega, Juan Q. Xavier'
>>> name.initials(spec="{given}{middle}{family}", delimiter="", separator="")
'JQXV'
>>> str(parse("DR. JUAN DE LA VEGA").capitalized())
'Dr. Juan de la Vega'
>>> str(parse("JuAn DE LA vEGA").capitalized())
'JuAn DE LA vEGA'
>>> str(parse("JuAn DE LA vEGA").capitalized(force=True))
'Juan de la Vega'
Looking for v1’s string_format? It’s the render(spec) argument
now — pass your own format string per call instead of setting it once
on a shared config object.
A spec chooses what the output is for. The default is written for
display and does not survive a reparse — it parenthesizes the maiden
name, which reads back as a nickname. When the rendered string will be
parsed again, spell the marker out (née {maiden}) so the field
round-trips; see the round-trip note in the tour.