为 unicode 字体初始化对象
Initialize object for unicode fonts
我写了一个 class 对象来访问 unicode 块中的数学字母数字符号,如 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
中所述
# San-serif
LATIN_SANSERIF_NORMAL_UPPER = (120224, 120250)
LATIN_SANSERIF_NORMAL_LOWER = (120250, 120276)
LATIN_SANSERIF_BOLD_UPPER = (120276, 120302)
LATIN_SANSERIF_BOLD_LOWER = (120302, 120328)
LATIN_SANSERIF_ITALIC_UPPER = (120328, 120354)
LATIN_SANSERIF_ITALIC_LOWER = (120354, 120380)
LATIN_SANSERIF_BOLDITALIC_UPPER = (120380, 120406)
LATIN_SANSERIF_BOLDITALIC_LOWER = (120406, 120432)
class MathAlphanumeric:
def __init__(self, script, font, style, case):
self.script = script
self.font = font
self.style = style
self.case = case
def charset(self):
start, end = eval('_'.join([self.script, self.font, self.style, self.case]).upper())
for c in range(start, end):
yield chr(c)
@staticmethod
def supported_scripts():
return {'latin', 'greek', 'digits'}
@staticmethod
def supported_fonts():
return {'serif', 'sanserif', 'calligraphy', 'fraktor', 'monospace', 'doublestruck'}
@staticmethod
def supported_style():
return {'normal', 'bold', 'italic', 'bold-italic'}
@staticmethod
def supported_case():
return {'upper', 'lower'}
要使用它,我会这样做:
ma = MathAlphanumeric('latin', 'sanserif', 'bold', 'lower')
print(list(ma.charset()))
[输出]:
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
代码按预期工作,但为了涵盖所有数学字母数字符号,我将不得不枚举 script * fonts * style * case
编号中的所有开始和结束符号。常数。
我的问题是:
- 是否有更好的方法来创建所需的
MathAlphanumeric
对象?
- 有没有办法避免初始化
script * fonts * style * case
没有。常数,以便 MathAlphanumeric.charset()
按预期工作?
- 在某些 unicode.org 相关库中是否有这样的对象或函数可用?
您可能对 unicodedata
标准库感兴趣,特别是:
unicodedata.lookup
:
Look up character by name. If a character with the given name is found, return the corresponding character. If not found, KeyError
is raised.
unicodedata.name
:
Returns the name assigned to the character chr as a string.
一个简单的例子:
>>> import unicodedata
>>> unicodedata.name(chr(0x1d5a0))
'MATHEMATICAL SANS-SERIF CAPITAL A'
>>> unicodedata.lookup("MATHEMATICAL SANS-SERIF CAPITAL A")
''
>>> unicodedata.name(chr(0x1d504))
'MATHEMATICAL FRAKTUR CAPITAL A'
>>> unicodedata.lookup("MATHEMATICAL FRAKTUR CAPITAL A")
''
现在您必须找到 unicodedata
期望用于您的用例的所有名称,从中构造相应的字符串,然后调用 lookup
.
这是一个小型概念验证:
import unicodedata
import string
def charset(script: str, font: str, style: str, case: str):
features = ["MATHEMATICAL"]
# TODO: use script
assert font in MathAlphanumeric.supported_fonts(), f"invalid font {font!r}"
features.append(font.upper())
assert style in MathAlphanumeric.supported_style(), f"invalid style {style!r}"
if style != "normal":
if font == "fraktur":
features.insert(-1, style.upper()) # "bold" must be before "fraktur"
elif font in ("monospace", "double-struck"):
pass # it has only one style, and it is implicit
else:
features.append(style.upper())
assert case in MathAlphanumeric.supported_case(), f"invalid case {case!r}"
features.append("CAPITAL" if case == "upper" else "SMALL")
return tuple(unicodedata.lookup(" ".join(features + [letter]), ) for letter in string.ascii_uppercase)
if __name__ == '__main__':
print("".join(charset("latin", "sans-serif", "bold", "lower")))
#
print("".join(charset("latin", "fraktur", "bold", "upper")))
#
print("".join(charset("latin", "monospace", "bold", "upper")))
#
print("".join(charset("latin", "double-struck", "bold", "upper")))
# KeyError: "undefined character name 'MATHEMATICAL DOUBLE-STRUCK CAPITAL C'"
(我稍微改变了你的 supported_fonts
方法:return {'serif', 'sans-serif', 'calligraphy', 'fraktur', 'monospace', 'double-struck'}
)
但 Unicode 中有很多注意事项:它包含您可能想要的所有字形,但没有以连贯的方式组织(由于历史原因)。我的示例中的失败是由以下原因引起的:
>>> unicodedata.name("") # the letter copied from the Wikipedia page
'MATHEMATICAL FRAKTUR CAPITAL B'
>>> unicodedata.name("ℭ") # same, but for C
'BLACK-LETTER CAPITAL C'
所以你会需要很多特殊情况。
还有:
- 使用
eval
被认为是一种不好的做法(参见 this question),如果可以避免,您应该避免。
- 使用 unicode 的十进制值“characters”不方便,我不得不将您的代码与维基百科页面进行十六进制转换。只需加上
0x
前缀就足以告诉 Python 它是一个十六进制值,但除了看起来“奇怪”之外,它的工作原理完全相同:0x1d5a0 == 120224
是 True。
- 仅使用一个从实例
__init__
获取其参数的方法的 class 被视为 smell, you can just make it a function, simpler and cleaner. If what you want is a namespace 您可以改用 Python 模块。
- 支持的脚本、字体、样式和大小写是不变的,您可以将它们设为 class 变量,而不是将它们放在
staticmethod
中。
我写了一个 class 对象来访问 unicode 块中的数学字母数字符号,如 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
中所述# San-serif
LATIN_SANSERIF_NORMAL_UPPER = (120224, 120250)
LATIN_SANSERIF_NORMAL_LOWER = (120250, 120276)
LATIN_SANSERIF_BOLD_UPPER = (120276, 120302)
LATIN_SANSERIF_BOLD_LOWER = (120302, 120328)
LATIN_SANSERIF_ITALIC_UPPER = (120328, 120354)
LATIN_SANSERIF_ITALIC_LOWER = (120354, 120380)
LATIN_SANSERIF_BOLDITALIC_UPPER = (120380, 120406)
LATIN_SANSERIF_BOLDITALIC_LOWER = (120406, 120432)
class MathAlphanumeric:
def __init__(self, script, font, style, case):
self.script = script
self.font = font
self.style = style
self.case = case
def charset(self):
start, end = eval('_'.join([self.script, self.font, self.style, self.case]).upper())
for c in range(start, end):
yield chr(c)
@staticmethod
def supported_scripts():
return {'latin', 'greek', 'digits'}
@staticmethod
def supported_fonts():
return {'serif', 'sanserif', 'calligraphy', 'fraktor', 'monospace', 'doublestruck'}
@staticmethod
def supported_style():
return {'normal', 'bold', 'italic', 'bold-italic'}
@staticmethod
def supported_case():
return {'upper', 'lower'}
要使用它,我会这样做:
ma = MathAlphanumeric('latin', 'sanserif', 'bold', 'lower')
print(list(ma.charset()))
[输出]:
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
代码按预期工作,但为了涵盖所有数学字母数字符号,我将不得不枚举 script * fonts * style * case
编号中的所有开始和结束符号。常数。
我的问题是:
- 是否有更好的方法来创建所需的
MathAlphanumeric
对象? - 有没有办法避免初始化
script * fonts * style * case
没有。常数,以便MathAlphanumeric.charset()
按预期工作? - 在某些 unicode.org 相关库中是否有这样的对象或函数可用?
您可能对 unicodedata
标准库感兴趣,特别是:
unicodedata.lookup
:Look up character by name. If a character with the given name is found, return the corresponding character. If not found,
KeyError
is raised.unicodedata.name
:Returns the name assigned to the character chr as a string.
一个简单的例子:
>>> import unicodedata
>>> unicodedata.name(chr(0x1d5a0))
'MATHEMATICAL SANS-SERIF CAPITAL A'
>>> unicodedata.lookup("MATHEMATICAL SANS-SERIF CAPITAL A")
''
>>> unicodedata.name(chr(0x1d504))
'MATHEMATICAL FRAKTUR CAPITAL A'
>>> unicodedata.lookup("MATHEMATICAL FRAKTUR CAPITAL A")
''
现在您必须找到 unicodedata
期望用于您的用例的所有名称,从中构造相应的字符串,然后调用 lookup
.
这是一个小型概念验证:
import unicodedata
import string
def charset(script: str, font: str, style: str, case: str):
features = ["MATHEMATICAL"]
# TODO: use script
assert font in MathAlphanumeric.supported_fonts(), f"invalid font {font!r}"
features.append(font.upper())
assert style in MathAlphanumeric.supported_style(), f"invalid style {style!r}"
if style != "normal":
if font == "fraktur":
features.insert(-1, style.upper()) # "bold" must be before "fraktur"
elif font in ("monospace", "double-struck"):
pass # it has only one style, and it is implicit
else:
features.append(style.upper())
assert case in MathAlphanumeric.supported_case(), f"invalid case {case!r}"
features.append("CAPITAL" if case == "upper" else "SMALL")
return tuple(unicodedata.lookup(" ".join(features + [letter]), ) for letter in string.ascii_uppercase)
if __name__ == '__main__':
print("".join(charset("latin", "sans-serif", "bold", "lower")))
#
print("".join(charset("latin", "fraktur", "bold", "upper")))
#
print("".join(charset("latin", "monospace", "bold", "upper")))
#
print("".join(charset("latin", "double-struck", "bold", "upper")))
# KeyError: "undefined character name 'MATHEMATICAL DOUBLE-STRUCK CAPITAL C'"
(我稍微改变了你的 supported_fonts
方法:return {'serif', 'sans-serif', 'calligraphy', 'fraktur', 'monospace', 'double-struck'}
)
但 Unicode 中有很多注意事项:它包含您可能想要的所有字形,但没有以连贯的方式组织(由于历史原因)。我的示例中的失败是由以下原因引起的:
>>> unicodedata.name("") # the letter copied from the Wikipedia page
'MATHEMATICAL FRAKTUR CAPITAL B'
>>> unicodedata.name("ℭ") # same, but for C
'BLACK-LETTER CAPITAL C'
所以你会需要很多特殊情况。
还有:
- 使用
eval
被认为是一种不好的做法(参见 this question),如果可以避免,您应该避免。 - 使用 unicode 的十进制值“characters”不方便,我不得不将您的代码与维基百科页面进行十六进制转换。只需加上
0x
前缀就足以告诉 Python 它是一个十六进制值,但除了看起来“奇怪”之外,它的工作原理完全相同:0x1d5a0 == 120224
是 True。 - 仅使用一个从实例
__init__
获取其参数的方法的 class 被视为 smell, you can just make it a function, simpler and cleaner. If what you want is a namespace 您可以改用 Python 模块。 - 支持的脚本、字体、样式和大小写是不变的,您可以将它们设为 class 变量,而不是将它们放在
staticmethod
中。