首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >给定电子数目的电子构形器

给定电子数目的电子构形器
EN

Code Review用户
提问于 2022-03-11 02:58:41
回答 2查看 220关注 0票数 6

我对编程很陌生,尤其是Python (从python3开始),我想问一问这段代码是否足够好。这是我个人项目的一部分,也就是建立一个关于化学的个人图书馆。此外,如果允许这样做,我想问一下,对于下面的代码,查找是否比计算更好。

这是一个函数,它可以给出给定电子数(electrons)的完整的或紧凑的电子构型。在这个函数中,完整的配置取决于紧凑的配置。

为了得到紧凑的构型,发现惰性元素中的电子数目最多,小于给定的电子数(inert_number)。作者相对于子subshells的“位置”是基于inert_number (subshell_index)确定的。在subshell_index中,给出了每个子壳层的电子数(base)。从inert_number中减去electrons,我们得到了copy。循环将启动,如果base >= copy结束,循环将结束。结果将是一个列表,从方括号(inert_head)中元素的原子符号开始,然后是子壳中的剩余电子。这方面的例外是具有不寻常配置的元素,它们的配置是通过查找找到的,前两个元素的紧凑配置不以原子符号开头。

为了获得完整的配置,将首先采用紧凑的配置。虽然配置的第一个元素是原子符号,但这个过程将是递归的。第一个元素将被接受,查找得到原子序数,然后找到紧凑的配置。结果将被附加以获得完整的配置。

代码语言:javascript
复制
inerts_number = [2, 10, 18, 36, 54, 86, 118]
inerts_config_index = [1, 3, 5, 8, 11, 15, 19]

atomic_symbols =  [
    "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", 
    "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", 
    "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", 
    "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", 
    "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", 
    "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", 
    "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", 
    "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", 
    "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", 
    "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", 
    "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", 
    "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"]

elems_with_odd_configs = [
    24, 29, 41, 42, 44, 
    45, 46, 47, 57, 58, 
    64, 78, 79, 89, 90, 
    91, 92, 93, 96]

configs_of_odds = [
    ["[Ar]", 1, 5],
    ["[Ar]", 1, 10],
    ["[Kr]", 1, 4], 
    ["[Kr]", 1, 5], 
    ["[Kr]", 1, 7],
    ["[Kr]", 1, 8],
    ["[Kr]", 0, 10],
    ["[Kr]", 1, 10],
    ["[Xe]", 2, 0, 1],
    ["[Xe]", 2, 1, 1],
    ["[Xe]", 2, 7, 1],
    ["[Xe]", 1, 14, 9],
    ["[Xe]", 1, 14, 10],
    ["[Rn]", 2, 0, 1],
    ["[Rn]", 2, 0, 2],
    ["[Rn]", 2, 2, 1],
    ["[Rn]", 2, 3, 1],
    ["[Rn]", 2, 4, 1],
    ["[Rn]", 2, 7, 1]]

subshell_value = [
    2, 2, 6, 2, 6, 
    2, 10, 6, 2, 10, 
    6, 2, 14, 10, 6, 
    2, 14, 10, 6]

def electron_config(electrons: int, config_type: str = 'compact') -> list:
    def __compact_config(electrons: int) -> list:
        electron_config = []
        inert_number = 0
        subshell_index = 0

        if electrons > 2:
            inert_number = max([x for x in inerts_number if x < electrons])
            subshell_index = inerts_config_index[inerts_number.index(inert_number)]
            inert_head = f'[{atomic_symbols[inert_number - 1]}]'
            electron_config.append(inert_head)
        else:
            inert_number = 0

        if electrons in elems_with_odd_configs:
            return configs_of_odds[elems_with_odd_configs.index(electrons)]
        else:
            copy = electrons - inert_number
            if electrons > 2:
                index = subshell_index
            else:
                copy = electrons
                index = 0
            while True:
                base = subshell_value[index]
                if base < copy:
                    electron_config.append(base)
                    copy -= base
                    index += 1
                else:
                    if copy > 0:
                        electron_config.append(copy)
                    break
            return electron_config
    
    def __full_config(electrons: int) -> list:
        temp_config = []
        compact_config = __compact_config(electrons)
        inert_elem = ''

        if electrons > 2:
            inert_elem = str(compact_config[0]).removeprefix('[').removesuffix(']')
            inert_electrons = atomic_symbols.index(inert_elem) + 1
            if inert_elem in ['He', 'Ne', 'Ar', 'Kr', 'Xe', 'Rn']:
                temp_config = __full_config(inert_electrons) + compact_config[1:]
        else:
            temp_config = __compact_config(electrons)

        return temp_config

    if config_type == 'compact':
        return __compact_config(electrons)
    elif config_type == 'full':
        return __full_config(electrons)
    else:
        raise ValueError(f'Expected \'compact\' or \'full\', received {config_type} instead')

另一种方法是将inert_numbersubshell_index作为可选参数添加到以None作为默认值的__compact_config中。如果两者都是None,请照常进行。然后,要获得完整的配置,将调用__compact_configinert_number = 0subshell_index = 0。这将是变化:

代码语言:javascript
复制
    ...
    def __compact_config(electrons: int, inert_number: int = None, subshell_index: int = None) -> list:
        ...
        if inert_number is None and subshell_index is None:
            if electrons > 2:
                inert_number = max([x for x in inerts_number if x < electrons])
                subshell_index = inerts_config_index[inerts_number.index(inert_number)]
                inert_head = f'[{atomic_symbols[inert_number - 1]}]'
                electron_config.append(inert_head)
            else:
                inert_number = 0
        ...
    def __full_config(electrons: int) -> list:
        return __compact_config(electrons, inert_number = 0, subshell_index = 0)

__full_config中,是使用递归方式来显示在寻找电子配置时的“通常”方式,还是从第一个子外壳开始,使用来自__compact_config的编写器?

EN

回答 2

Code Review用户

回答已采纳

发布于 2022-03-11 13:42:34

数据库文本(atomic_symbols等)应该都是元组(),而不是列表[],因为它们是不可变的。然而,正如我下面所展示的,有些更适合于字典而不是序列。

您的electron_config函数实际上只是两个函数:一个用于紧凑型,另一个用于完整。如果要将它们保持为一个函数,则不应暗示config_typestr,而应暗示为Literal['compact', 'full']。但这两部分毫无逻辑可言。最好将两个内部函数提取为外部函数。此外,这些内部函数--因为它们在函数范围内--目前对外部是不可见的,所以用下划线作为前缀是没有意义的。

inert_elem in ['He', 'Ne', 'Ar', 'Kr', 'Xe', 'Rn']应该使用set文本{},并且应该将该文本移动到与其他数据库常量相同的位置。

这是:

代码语言:javascript
复制
.removeprefix('[').removesuffix(']')

是元素名称存在过早格式化的证据。我不明白为什么要将元素名括在括号中,但如果必须的话,应该在后面的格式化函数中完成。

您的返回格式很奇怪:它是“可能”字符串,后面是整数列表。最好把这个分割成一个

代码语言:javascript
复制
tuple[
    Optional[str],
    list[int]
]

或者,更好的方法是用生成器替换列表,不要连续使用-append()

添加单元测试。我只做了回归测试。

您的一些数据结构模式并不理想。例如,elems_with_odd_configs实际上应该是作为字典重新分解的configs_of_odds的键。您的max搜索也不理想,应该用对bisect_left的调用来替换,以便在对数中而不是线性时间中找到最合适的值(尽管考虑到序列有多小,性能并不明显)。尽可能避免.index()

与其将自己的index维护到SUBSHELL_VALUE中,不如在一个片段上迭代base

一个包含上述部分内容的轻重构是

代码语言:javascript
复制
from typing import Optional, Iterable, Iterator
from bisect import bisect_left

INERTS_CONFIG_INDEX = (1, 3, 5, 8, 11, 15, 19)
INERTS_NUMBER = (2, 10, 18, 36, 54, 86, 118)

ATOMIC_SYMBOLS = (
    'H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne',
    'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca',
    'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn',
    'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr',
    'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn',
    'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd',
    'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb',
    'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg',
    'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th',
    'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm',
    'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds',
    'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og',
)

CONFIGS_OF_ODDS = {
    24: ('Ar', (1, 5)),
    29: ('Ar', (1, 10)),
    41: ('Kr', (1, 4)),
    42: ('Kr', (1, 5)),
    44: ('Kr', (1, 7)),
    45: ('Kr', (1, 8)),
    46: ('Kr', (0, 10)),
    47: ('Kr', (1, 10)),
    57: ('Xe', (2, 0, 1)),
    58: ('Xe', (2, 1, 1)),
    64: ('Xe', (2, 7, 1)),
    78: ('Xe', (1, 14, 9)),
    79: ('Xe', (1, 14, 10)),
    89: ('Rn', (2, 0, 1)),
    90: ('Rn', (2, 0, 2)),
    91: ('Rn', (2, 2, 1)),
    92: ('Rn', (2, 3, 1)),
    93: ('Rn', (2, 4, 1)),
    96: ('Rn', (2, 7, 1)),
}

SUBSHELL_VALUE = (
    2, 2, 6, 2, 6,
    2, 10, 6, 2, 10,
    6, 2, 14, 10, 6,
    2, 14, 10, 6,
)

INERT_ELEMS = {
    'He', 'Ne', 'Ar', 'Kr', 'Xe', 'Rn',
}


def generate_electrons(index: int, copy: int) -> Iterator[int]:
    for base in SUBSHELL_VALUE[index:]:
        if base >= copy:
            if copy > 0:
                yield copy
            break
        yield base
        copy -= base


def make_compact_config(electrons: int) -> tuple[
    Optional[str],  # inert head
    Iterable[int],  # electron counts
]:
    odd_config = CONFIGS_OF_ODDS.get(electrons)
    if odd_config is not None:
        return odd_config

    if electrons <= 2:
        return None, generate_electrons(index=0, copy=electrons)

    inert_index = bisect_left(INERTS_NUMBER, electrons) - 1
    inert_number = INERTS_NUMBER[inert_index]
    subshell_index = INERTS_CONFIG_INDEX[inert_index]
    inert_head = ATOMIC_SYMBOLS[inert_number - 1]
    return inert_head, generate_electrons(index=subshell_index, copy=electrons - inert_number)


def make_full_config(electrons: int) -> Iterator[int]:
    inert_elem, compact_config = make_compact_config(electrons)

    if electrons > 2:
        inert_electrons = ATOMIC_SYMBOLS.index(inert_elem) + 1
        if inert_elem in INERT_ELEMS:
            yield from make_full_config(inert_electrons)

    yield from compact_config


def test() -> None:
    inert_head, config = make_compact_config(1)
    assert inert_head is None
    assert tuple(config) == (1,)

    inert_head, config = make_compact_config(10)
    assert inert_head == 'He'
    assert tuple(config) == (2, 6)

    inert_head, config = make_compact_config(42)
    assert inert_head == 'Kr'
    assert tuple(config) == (1, 5)

    inert_head, config = make_compact_config(100)
    assert inert_head == 'Rn'
    assert tuple(config) == (2, 12)

    assert tuple(make_full_config(1)) == (1,)

    assert tuple(make_full_config(10)) == (2, 2, 6)

    assert tuple(make_full_config(42)) == (2, 2, 6, 2, 6, 2, 10, 6, 1, 5)

    assert tuple(make_full_config(100)) == (2, 2, 6, 2, 6, 2, 10, 6, 2, 10, 6, 2, 14, 10, 6, 2, 12)


if __name__ == '__main__':
    test()
票数 7
EN

Code Review用户

发布于 2022-03-11 20:56:17

不要将核心算法和表示问题混合在一起。您的算法构建像[He]这样的字符串来容纳惰性的头部。但是,在代码的其他部分中,您必须反转这样的字符串操作才能得到He。不要这样做,而是围绕基本的价值观或重要性来组织数据结构和值。稍后,出于演示的目的,您可能需要将方括号粘合到惰性的头部,但在游戏后期,而不是在基本计算过程中,这很容易做到。

适当地命名常量。您的程序有几个重要的常量,但它们被命名为类似变量。

组织数据结构以方便算法,第1部分。您当前的数据结构非常尴尬。您有一个atomic_symbols列表,但您从不直接使用该列表。你真正需要的是从电子到符号的查找。

代码语言:javascript
复制
SYMS =  (
    'H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne',
    'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca',
    'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn',
    'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr',
    'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn',
    'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd',
    'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb',
    'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg',
    'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th',
    'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm',
    'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds',
    'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og',
)

SYMBOLS = dict(zip(range(1, len(SYMS) + 1), SYMS))

组织数据结构以方便算法,第2部分。您的一些其他常量会引起麻烦,因为它们需要笨拙的索引查找和对并行数据结构的引用。以下是两个例子:

代码语言:javascript
复制
# Example 1.
subshell_index = inerts_config_index[inerts_number.index(inert_number)]

# Example 2.
return configs_of_odds[elems_with_odd_configs.index(electrons)]

只要对代码进行一些调整,简单得多的东西就能正常工作:

代码语言:javascript
复制
# Just the numbers are sufficient.
# You don't need inerts_config_index.
INERT_NUMBERS = (2, 10, 18, 36, 54, 86, 118)

# I don't know your domain well, so adjust naming as needed.
SUBSHELL_VALUES = (
    2,
    2, 6,
    2, 6,
    2, 10, 6,
    2, 10, 6,
    2, 14, 10, 6,
    2, 14, 10, 6,
)

# Create a dict so you can directly access the special compact-configs
# rather than jumping through hoops via index lookups.
SPECIAL_COMPACTS = {
    24: (1, 5),
    29: (1, 10),
    41: (1, 4),
    42: (1, 5),
    44: (1, 7),
    45: (1, 8),
    46: (0, 10),
    47: (1, 10),
    57: (2, 0, 1),
    58: (2, 1, 1),
    64: (2, 7, 1),
    78: (1, 14, 9),
    79: (1, 14, 10),
    89: (2, 0, 1),
    90: (2, 0, 2),
    91: (2, 2, 1),
    92: (2, 3, 1),
    93: (2, 4, 1),
    96: (2, 7, 1),
}

创建一个有意义的对象来存储电子配置。目前,您的代码要求用户请求完整的配置或紧凑的配置。但是这两者是密切相关的(一种是另一种子集),因此返回某种数据结构或对象似乎更简单,这些数据结构或对象可以同时容纳两个信任。此外,您当前的紧凑-信任是尴尬的,因为它们将不同的数据放在一个集合中(一个字符串加上一些数字)。最后,你的电子吐露缺少一些有用的辅助信息:电子数量和原子符号等事实。在下面的代码中,我使用一个类将所有东西捆绑在一起。如果您现在想要避免OO,您可以返回类似于dict的内容。无论哪种方式,重点都是创建一组有意义的声明性信息。

计算信任的算法比需要的要复杂得多。我知道您对当前代码中创建完整和紧凑的信任的替代方法的反馈特别感兴趣。但是,我认为您当前的代码太复杂了。相反,我鼓励你们考虑一种不同的方法--一种根本不需要递归的方法。逻辑经历了三个阶段:(1)纯基于SUBSHELL_VALUES的朴素的完整配置;(2)朴素的压缩-配置,从这个朴素的完整配置中弹出值;(3)解析阶段,如果有的话,我们使用特殊的compact,并相应地调整full-config。

代码语言:javascript
复制
class ElectronConfig:

    def __init__(self, electrons):
        # Symbol and N of electrons.
        self.symbol = SYMBOLS[electrons]
        self.electrons = electrons

        # Inert head.
        inerts = [n for n in INERT_NUMBERS if n < electrons]
        inert_number = max(inerts, default = 0)
        self.inert_head = SYMBOLS.get(inert_number, None)

        # A naive full-config based only on SUBSHELL_VALUES.
        full = []
        tot = 0
        for ssv in SUBSHELL_VALUES:
            tot += ssv
            full.append(ssv)
            diff = tot - electrons
            if diff >= 0:
                full[-1] -= diff
                break

        # A naive compact-config, created by removing values
        # from the end of the naive full-config.
        compact = []
        tot = 0
        while tot < electrons - inert_number:
            ssv = full.pop()
            tot += ssv
            compact.append(ssv)
        compact.reverse()

        # Set the actual configs.
        self.compact = SPECIAL_COMPACTS.get(electrons, tuple(compact))
        self.full = tuple(full) + self.compact
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/274820

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档