当前位置: 首页 > news >正文

隧道建设网站无法登录网站建设具体方案

隧道建设网站无法登录,网站建设具体方案,ui培训报名,网站设计的公司如何选一、迭代器和生成器 区别 关于迭代器和生成器 迭代器与生成器的区别 迭代器#xff08;Iterator#xff09;和生成器#xff08;Generator#xff09;是Python中处理序列数据的两种不同概念。迭代器是遵循迭代协议的对象#xff0c;而生成器是一种特殊类型的迭代器Iterator和生成器Generator是Python中处理序列数据的两种不同概念。迭代器是遵循迭代协议的对象而生成器是一种特殊类型的迭代器它通过函数定义并使用yield关键字来产生值。 迭代器 1.迭代器是一个对象它实现了迭代器协议即拥有__iter__()和__next__()方法。 2.iter()方法返回迭代器对象本身。 3.next()方法返回容器的下一个元素当没有更多元素时应抛出StopIteration异常。 4.迭代器可以通过内置的iter()函数创建或者通过实现__iter__()和__next__()方法的类来创建。 5.迭代器可以使用for循环进行遍历也可以通过next()函数逐一访问元素。 生成器 1.生成器是一种迭代器它通过使用yield关键字定义的函数来创建。 2.生成器函数在每次遇到yield时暂停执行并返回一个值下次迭代时从暂停的地方继续执行。 3.生成器函数的执行不是一次性完成的而是按需计算这有助于节省内存尤其适用于处理大数据集。 4.生成器可以通过在函数定义中包含yield关键字来创建或者使用生成器表达式()来创建。 5.生成器对象可以直接用于for循环也可以通过next()函数进行迭代。 总结 1.迭代器是遵循特定协议的对象可以通过多种方式创建。 2.生成器是迭代器的一种通过函数定义使用yield关键字来实现按需计算和状态保存。 3.生成器具有内存效率高的优点特别适合处理大型数据集。 在实际应用中生成器因其惰性计算的特性而被广泛使用尤其是在处理可能导致内存溢出的大数据集时. 迭代器与生成器的区别 迭代器Iterator和生成器Generator都是Python中用于迭代操作的工具但它们在实现细节和使用场景上有所不同。 定义和实现方式 1.迭代器是实现了迭代器协议的对象即必须实现__iter__()和__next__()方法的对象。迭代器可以通过类来定义该类需要显式实现这两个方法。 2.生成器是使用yield关键字定义的函数当这个函数被调用时它返回一个生成器对象。生成器函数在每次遇到yield时暂停执行并在下次迭代时从暂停的地方继续执行从而实现了迭代的功能。 状态保持 1.迭代器在每次调用__next__()方法时返回序列的下一个元素但它不自动保存状态。如果需要在迭代过程中保持状态需要在__next__()方法中显式实现。 2.生成器在每次产生一个值后会自动保存当前的状态下次调用时会从上次离开的地方继续执行。这使得生成器在处理复杂迭代逻辑时更为方便。 内存效率 1.迭代器通常需要在内存中存储所有待迭代的元素尤其是在使用传统的迭代器类实现时。 2.生成器由于其惰性计算的特性只在迭代过程中按需产生元素因此通常比迭代器更节省内存特别适用于处理大数据集。 可重用性 1.迭代器理论上可以被多次迭代尽管在实际使用中很少这么做因为大多数迭代器设计为一次性迭代。 2.生成器通常只能被迭代一次因为它们不保存整个序列只保存当前的状态和上下文。 灵活性 1.迭代器需要在__next__()方法中实现所有的控制流逻辑这可能导致代码较为复杂。 2.生成器更加灵活可以使用任何种类的控制流语句因为它们的状态是由函数的执行流程自然管理的。 综上所述生成器是一种特殊的迭代器它们提供了一种更简洁、内存效率更高且易于实现复杂迭代逻辑的方式。在需要处理大型数据集或在迭代过程中动态生成数据时生成器是更佳的选择. 使用生成器来迭代一个列表可以提高内存效率尤其是在处理大数据集时。下面是一个使用生成器函数来迭代列表的例子 def list_generator(lst):for item in lst:yield item# 创建一个列表 my_list [1, 2, 3, 4, 5]# 创建一个生成器 gen list_generator(my_list)# 使用生成器 for i in gen:print(i)但是更常见的做法是直接使用列表生成器表达式或在for循环中直接使用列表因为Python的迭代器机制已经足够高效生成器的主要优势在于处理无法一次性加载到内存中的大量数据。对于普通大小的列表直接迭代通常更为简便。例如 my_list [1, 2, 3, 4, 5]# 使用列表生成器表达式 gen_expr (x for x in my_list) for i in gen_expr:print(i)# 或者直接在 for 循环中迭代 for i in my_list:print(i)在上述代码中使用生成器表达式gen_expr (x for x in my_list)可以创建一个生成器然后通过for循环来迭代这个生成器每次迭代都会按需生成并返回列表中的下一个元素。 使用生成器或生成器表达式迭代列表的关键在于理解和利用yield关键字它使得函数能够暂停并保存当前状态然后在下一次调用时从上次停止的地方继续执行。这对于处理大数据集或需要进行复杂迭代逻辑的场景非常有帮助。 生成器和迭代器的区别 生成器Generators和迭代器Iterators是Python中用于处理数据序列的两种不同概念。它们都遵循迭代器协议即实现了__iter__()和__next__()方法但它们的实现方式和用途有所不同。 工作方式 1.迭代器是一个对象它实现了迭代器协议可以通过__iter__()方法返回自身并通过__next__()方法返回序列中的下一个元素。当没有更多元素时next()方法会抛出StopIteration异常。 2.生成器是一种特殊类型的迭代器它通过函数定义并使用yield关键字来产生值。生成器函数在每次遇到yield时暂停执行并保存当前状态下次调用__next__()方法时函数会从上次暂停的位置继续执行。 内存占用 1.迭代器在遍历时会加载整个数据集合到内存中如果序列很大可能会占用大量内存。 2.生成器以惰性求值的方式生成数据只在需要时计算和返回值因此内存占用较低特别适用于处理大型数据集。 实现方式 1.迭代器可以通过自定义迭代器类来实现需要显式定义__iter__()和__next__()方法。 2.生成器可以通过生成器函数包含yield语句的函数或生成器表达式类似列表推导式但返回生成器对象来创建生成器函数提供了一种更为简洁的方式来动态生成值。 可变性 1.迭代器在迭代过程中是不可变的。 2.生成器在生成过程中是不可变的但在生成结束后可以修改原始函数。 惰性求值 1.迭代器和生成器都支持惰性求值即它们按需生成数据这有助于节省内存。 无限序列 1.生成器可以用来创建无限序列因为它们只在需要时计算下一个值不会耗尽内存。 总结 生成器是迭代器的一种更高级形式它们提供了一种更高效的方式来处理数据序列尤其是在处理大型或无限数据集时。生成器的惰性求值特性使得它们在内存使用上更加经济而且生成器函数的语法比自定义迭代器类更为简洁. 自定义迭代器 创建自定义迭代器来迭代一个列表可以让你在迭代过程中加入更复杂的逻辑或状态。下面是一个使用自定义迭代器类来迭代列表的例子该迭代器将在每次迭代时返回列表中元素的平方 class SquareIterator:def __init__(self, lst):self.lst lstself.index 0def __iter__(self):return selfdef __next__(self):if self.index len(self.lst):raise StopIterationresult self.lst[self.index] ** 2self.index 1return result# 创建一个列表 my_list [1, 2, 3, 4, 5]# 创建自定义迭代器实例 squares SquareIterator(my_list)# 使用自定义迭代器 for square in squares:print(square)在这个例子中 1.SquareIterator类定义了迭代器的逻辑。 2.__init__方法初始化迭代器保存列表和当前索引。 3.__iter__方法返回迭代器自身这是迭代器协议的一部分。 4.__next__方法返回列表中下一个元素的平方。如果索引超出范围它将抛出StopIteration异常结束迭代。 通过这种方式你可以根据需要定制迭代逻辑比如处理更复杂的数据转换、状态跟踪等。使用自定义迭代器可以让你的代码更清晰地表达迭代过程中的逻辑特别是在处理非线性或条件依赖的迭代需求时。 使用自定义迭代器可以带来多种潜在好处主要包括 控制数据流 自定义迭代器允许你精确控制数据的生成和消费过程。这意味着你可以在迭代过程中根据需要计算或检索数据而不是一次性将所有数据加载到内存中这对于处理大型数据集尤为重要。 增加代码的灵活性和可重用性 通过实现自定义迭代器你可以为自定义数据结构定义迭代行为使得这些结构可以像内置数据类型一样被迭代。这提高了代码的灵活性和可重用性。 优化内存使用 由于自定义迭代器通常采用惰性计算它们可以按需生成数据从而减少内存占用。这对于内存受限的系统或需要处理大量数据的应用来说是非常有利的。 实现复杂的迭代逻辑 自定义迭代器可以维护内部状态允许在迭代过程中执行复杂的逻辑如状态跟踪、计数器管理等。这为解决特定的编程问题提供了更多的可能性。 支持协程和异步编程 自定义迭代器可以用作协程的基础支持异步编程模式。这在需要处理并发任务或实时数据流的应用中非常有用。 促进模块化和封装 通过封装迭代逻辑到迭代器类中可以将数据的表示和迭代分离降低模块之间的耦合度提高代码的模块化水平。 综上所述自定义迭代器不仅可以提高程序的性能和效率还可以增强代码的可维护性和扩展性。在设计算法和数据处理流程时考虑使用自定义迭代器是一个值得推荐的做法。 自定义生成器 在Python中自定义生成器是一种非常实用的功能它允许你以函数的形式定义一个可以返回一系列值的迭代器而不需要显式创建一个列表或集合。生成器通过使用yield关键字来实现这使得函数在每次调用next()方法时生成一个值然后暂停其状态直到下次迭代时恢复。 如何自定义生成器 自定义生成器的基本结构是一个包含yield关键字的函数。下面是一个简单的例子展示如何自定义一个生成器来生成斐波那契数列的前N个数字。 def fibonacci(n):a, b 0, 1for _ in range(n):yield aa, b b, a b# 创建生成器 fib fibonacci(10)# 迭代生成器 for num in fib:print(num)在这个例子中fibonacci函数就是一个自定义生成器它使用yield来返回斐波那契数列中的每一个数字。 使用自定义生成器的好处 1.内存效率生成器只在需要时生成值不需要一次性将所有数据存储在内存中。 2.惰性求值生成器遵循按需计算的原则只有在需要下一个值时才计算它。 3.状态保存生成器可以保存其执行状态这使得它可以在多次调用next()方法时从上次停止的位置继续执行。 自定义生成器的其他应用 自定义生成器可以用于各种复杂的迭代逻辑例如在读取大文件时逐行处理或者在处理网络请求时按需生成数据。它们也可以用于实现复杂的算法如深度优先搜索DFS、广度优先搜索BFS等。 结论 自定义生成器是Python中一个强大的特性它允许你以简洁、高效的方式处理数据流和迭代任务。通过使用yield关键字你可以轻松地将一个函数转化为一个可以生成一系列值的迭代器而无需显式地管理状态或使用额外的内存。 深度优先搜索DFS的基本概念 深度优先搜索DFS是一种用于遍历或搜索树或图的算法。它尝试深入图的各个路径尽可能远地搜索直到遇到终点节点或达到某个深度限制。如果当前路径不可达终点算法将回溯到最近的分叉点尝试另一条路径。DFS通常使用递归或栈来实现。 自定义生成器实现DFS的步骤 1.定义节点类创建一个表示图中节点的类该类可以包含节点的值、邻居节点列表以及一个标志位来指示节点是否已被访问。 2.实现生成器函数编写一个生成器函数该函数接受图的邻接表和起始节点作为参数。在生成器函数中使用递归或迭代结合栈来模拟深度优先遍历的过程。 3.使用yield关键字在生成器函数中使用yield关键字来逐个产生遍历过程中访问的节点。每当函数遇到一个未访问的邻居节点时它会产生该节点并继续遍历直到达到叶子节点或回溯。 4.处理回溯在递归或迭代过程中当当前路径无法继续时需要进行回溯。在生成器中这通常通过跳出当前递归调用或从栈中移除节点来实现。 5.返回生成器对象最后生成器函数返回一个生成器对象该对象可以通过迭代来访问所有被DFS遍历的节点。 代码示例 以下是一个使用自定义生成器实现DFS的简单代码示例 class Node:def __init__(self, value):self.value valueself.neighbors []self.visited Falsedef dfs_generator(root):def traverse(node):if node.visited:returnnode.visited Trueyield node.valuefor neighbor in node.neighbors:yield from traverse(neighbor)return traverse(root)# 使用示例 root Node(A) root.neighbors [B, C] B Node(B) B.neighbors [D, E] C Node(C) C.neighbors [F] D Node(D) E Node(E) F Node(F)# 连接节点 root.neighbors.extend([B, C]) B.neighbors.extend([root, D, E]) C.neighbors.extend([root, F]) D.neighbors.extend([B]) E.neighbors.extend([B, F]) F.neighbors.extend([C, E])# 执行DFS for node_value in dfs_generator(root):print(node_value)在这个示例中dfs_generator函数定义了DFS的生成器逻辑它使用了一个内部函数traverse来实现递归遍历。生成器函数返回的是一个迭代器可以通过循环来访问所有被遍历的节点值。 优势和注意事项 使用自定义生成器实现DFS的优势在于能够以惰性的方式遍历图这意味着只有在迭代过程中实际需要节点值时节点才会被访问和处理。这有助于节省内存尤其是在处理大规模图时。此外生成器的使用可以使代码更加清晰和模块化。 在实现时需要注意保持状态的一致性确保每个节点在被访问之前不会被重复访问。同时生成器的使用可能会影响调试过程因为它不像普通函数那样可以直接通过打印语句来观察中间状态。因此可能需要额外的逻辑来跟踪和打印遍历过程中的关键信息。 yield from的使用 yield from是Python中用于生成器的一个关键字它允许一个生成器将另一个生成器的迭代结果直接传递给调用者。yield from表达式可以看作是一个高级的委托操作使得生成器可以像调用子程序一样调用另一个生成器同时保持迭代的流畅性和效率。 yield from的基本用法 当在生成器中使用yield from时它会暂停当前生成器的执行转而执行被调用的生成器直到被调用的生成器完成或被显式停止。一旦被调用的生成器完成迭代控制权会返回到yield from表达式所在的生成器继续执行后续的代码。 举例说明 假设我们有两个生成器gen1和gen2 def gen1():yield oneyield twoyield threedef gen2():yield fouryield fivedef combined_gen():yield from gen1()yield intermediateyield from gen2()# 使用combined_gen for item in combined_gen():print(item) 运行上述代码会输出 one two three intermediate four five在这个例子中combined_gen使用yield from从gen1和gen2中直接产生值而无需显式地将它们一个一个地yield出来。 yield from的优势 1.代码简洁性使用yield from可以避免手动遍历被调用的生成器并逐个yield值的繁琐操作从而让代码更简洁、更易于理解。 2.效率提升yield from避免了中间列表的创建直接将迭代值传递给调用者提高了迭代的效率。 3.传递异常和发送值yield from还支持异常传递和send()方法的值传递使得生成器之间的交互更加灵活和强大。 总结 yield from是Python中用于生成器间传递和委托迭代的强大工具它简化了生成器的编写和迭代的处理同时保持了代码的清晰性和执行的效率。通过它可以轻松地将一个生成器的迭代结果嵌入到另一个生成器中实现复杂的迭代逻辑和数据流控制。 使用自定义迭代器实现深度优先搜索DFS 深度优先搜索DFS是一种用于遍历或搜索树或图的算法它沿着树的深度遍历树的节点尽可能深地搜索树的分支。在Python中可以使用自定义迭代器来实现DFS这种方法通常涉及到使用栈来模拟递归过程。 自定义迭代器的设计 自定义迭代器通常需要实现__iter__()和__next__()方法。在DFS的上下文中迭代器将维护一个栈来追踪搜索路径并在__next__()方法中实现搜索逻辑包括节点的访问、栈的管理以及回溯机制。 实现步骤 1.初始化栈和已访问集合创建一个空栈来存放待访问的节点以及一个集合来记录已访问的节点。 2.定义迭代器的__iter__()方法返回迭代器自身以便可以多次迭代。 3.定义迭代器的__next__()方法在每次迭代中从栈顶弹出节点检查该节点是否已访问。如果未访问则访问该节点并将其未访问的邻居节点压入栈中。如果已访问或栈为空则抛出StopIteration异常表示迭代结束。 4.实现搜索逻辑在__next__()方法中使用栈来模拟递归DFS的回溯行为。 代码示例 以下是一个使用自定义迭代器实现DFS的简单代码示例 class GraphNode:def __init__(self, value):self.value valueself.neighbors []class DFSIterator:def __init__(self, graph, start):self.graph graphself.start startself.visited set()self.stack [start]def __iter__(self):return selfdef __next__(self):if not self.stack:raise StopIterationcurrent self.stack.pop()if current not in self.visited:self.visited.add(current)self.stack.extend(node for node in reversed(self.graph[current]) if node not in self.visited)return current# 使用示例 graph {A: {B, C},B: {D, E},C: {F},D: {},E: {F},F: {} }for node in DFSIterator(graph, A):print(node)在这个示例中GraphNode类表示图中的节点DFSIterator类实现了自定义的DFS迭代器。next()方法中的逻辑确保了每次迭代都会从栈中弹出一个未访问的节点并更新栈和已访问集合。 注意事项 1.确保在__next__()方法中正确管理栈和已访问集合以避免重复访问节点和无限循环。 2.自定义迭代器提供了一种优雅的方式来处理递归深度可能引起的栈溢出问题尤其是在处理大型图时。 3.使用迭代器可以使代码更加模块化便于测试和重用。 以上步骤和代码示例展示了如何使用自定义迭代器在Python中实现DFS。这种方法利用了Python的迭代器协议提供了一种灵活和高效的遍历图的方式. 二、闭包和装饰器 关于python闭包 闭包在Python中能够实现一些特殊的功能以下是几个经典例子来展示闭包的用法 计数器 闭包可以用来创建一个简单的计数器能够保存和更新一个变量的值即使函数已经执行结束。 def counter():count 0def increment():nonlocal countcount 1return countreturn incrementcounter_func counter() print(counter_func()) # 输出 1 print(counter_func()) # 输出 2求和器 闭包可以用来保存累加的状态实现一个累加器。 def accumulator():total 0def add(value):nonlocal totaltotal valuereturn totalreturn addadd_func accumulator() print(add_func(5)) # 输出 5 print(add_func(10)) # 输出 15私有变量 闭包可以用来创建私有变量即在外部无法直接访问的变量。 def private_variable():_private_var 10def access():return _private_varreturn accessaccess_func private_variable() print(access_func()) # 输出 10 # _private_var 无法在外部访问懒加载 闭包可以用来实现懒加载lazy evaluation即在需要时才计算结果。 def lazy_evaluation():result Nonedef compute():nonlocal resultif result is None:result 1 2 # 假设这是一个复杂的计算过程return resultreturn computecompute_func lazy_evaluation() print(compute_func()) # 输出 3 # 第二次调用时不再计算 print(compute_func()) # 输出 3这些例子展示了闭包在Python中的一些典型用途包括状态保存、私有变量、累加以及懒加载。闭包的使用使代码更加灵活和高效。 关于Python装饰器 Python装饰器是一种设计模式它允许在不修改原有函数代码的情况下动态地添加新的功能。装饰器的使用带来了多种优势 代码复用性装饰器可以封装通用的功能使得这些功能可以被多个函数重用减少了代码冗余。维护简便由于装饰器不直接修改原有函数的源代码因此在添加或修改功能时更加集中和清晰便于维护和更新。易于测试独立的装饰器可以单独进行测试确保它们的功能正确无误这有助于整体代码的质量控制。解耦关注点装饰器可以将横切关注点如日志记录、性能监控、事务管理等与业务逻辑分离提高了代码的模块化和解耦程度。可读性和表达力使用装饰器可以使代码更加简洁和直观特别是当多个装饰器组合使用时可以清晰地表达出函数的多重职责。灵活性装饰器可以接受参数这使得它们在应用时具有高度的灵活性可以根据不同的需求调整其行为。自动注册和发现装饰器可以自动将函数注册到数据结构中便于程序的自省和动态发现这在构建插件系统或依赖注入框架时尤为有用。兼容性装饰器可以无缝地与其他高级特性如类、生成器等结合使用提供了广泛的应用场景。 通过这些优势Python装饰器成为了提高代码质量、促进开发效率和维护便捷性的有力工具。 以下是几个使用Python装饰器的实用示例每个示例都展示了装饰器在不同场景下的应用 示例1日志记录装饰器 def log_function_call(func):def wrapper(*args, **kwargs):print(fCalling function {func.__name__} with args {args} and kwargs {kwargs})result func(*args, **kwargs)print(fFunction {func.__name__} returned {result})return resultreturn wrapperlog_function_call def add(a, b):return a b# 调用函数 add(5, 3)示例2性能计时装饰器 import timedef timeit(func):def wrapper(*args, **kwargs):start time.time()result func(*args, **kwargs)end time.time()print(f{func.__name__} took {end - start:.6f} seconds to run)return resultreturn wrappertimeit def slow_function():time.sleep(2)return done# 调用函数 slow_function()示例3权限检查装饰器 def requires_permission(permission):def decorator(func):def wrapper(*args, **kwargs):if permission in args[0].permissions:return func(*args, **kwargs)else:raise PermissionError(Permission not granted)return wrapperreturn decoratorclass User:def __init__(self, permissions[]):self.permissions permissionsrequires_permission(admin)def admin_action(self):print(Admin action performed)# 创建一个用户并测试 user User(permissions[admin]) user.admin_action()示例4缓存结果装饰器 from functools import lru_cachelru_cache(maxsizeNone) def fibonacci(n):if n 2:return nreturn fibonacci(n-1) fibonacci(n-2)# 调用函数 print(fibonacci(30))这些示例分别展示了如何使用装饰器进行日志记录、性能监控、权限检查和结果缓存体现了装饰器的强大功能和灵活性。 区别 Python装饰器确实利用了闭包的概念。闭包是指一个函数能够记住并访问其所在上下文中的变量即使这个函数在其外部函数执行完毕后仍然存在。在Python中装饰器通常是一个接受函数作为参数并返回一个新函数的函数。这个返回的新函数能够记住并访问装饰器内部定义的变量即使装饰器本身已经执行完毕。 装饰器的工作机制涉及到嵌套函数其中内部函数装饰器返回的函数能够访问并操作外部函数装饰器本身的局部变量。这种结构允许装饰器向被装饰的函数添加额外的功能而无需修改原始函数的源代码。装饰器的这种特性正是闭包的一个典型应用。 三、性能优化多线程 Python的GIL全局锁 Python的全局解释器锁Global Interpreter Lock简称GIL是CPythonPython的主流实现中的一个机制它确保在任何时刻只有一个线程可以执行Python字节码。GIL的存在是为了简化Python解释器的实现尤其是在内存管理和垃圾回收方面但它也限制了多线程程序在执行CPU密集型任务时的性能. GIL的工作机制 GIL作为一个互斥锁在执行Python字节码之前获取并锁定全局解释器锁从而阻止其他线程执行Python字节码。一旦某个线程获取了GIL它将独占解释器并在执行完一定数量的字节码或者时间片后将GIL释放使其他线程有机会获取GIL并执行字节码。这个过程在多个线程之间不断重复以实现多线程的执行. GIL对性能的影响 对于CPU密集型任务GIL成为了性能瓶颈因为在同一时刻只有一个线程可以执行Python代码无法充分利用多核处理器的并行计算能力。而对于I/O密集型任务GIL的影响较小因为线程在等待I/O操作完成时会释放GIL允许其他线程在等待I/O的过程中继续执行. GIL的替代方案 为了克服GIL的限制开发者可以采取多种策略如使用多进程代替多线程因为每个进程都有独立的GIL和内存空间能够有效地利用多核CPU的并行计算能力。此外还可以使用异步编程模型如asyncio通过事件循环和回调机制来调度任务避免了多线程中的GIL竞争. GIL的历史背景 GIL的设计初衷是为了简化Python解释器的实现特别是在Python最初设计时操作系统还没有线程概念Python被设计为易于使用的脚本语言主要应用在单线程的环境下。随着时间的推移尽管多核处理器变得普遍但GIL仍然存在于CPython中因为移除GIL可能会导致兼容性问题和内存管理的复杂化. 社区对GIL的看法 GIL在Python社区中一直是一个有争议的话题。一方面GIL简化了Python的内存管理使得Python在单线程环境中易于使用另一方面它限制了多线程程序的性能尤其是在科学计算和高性能计算领域。社区中有关于移除GIL的讨论但由于其在CPython实现中的深度集成这一改变面临着重大挑战. 综上所述GIL是Python多线程编程中的一个核心概念理解其工作机制和对性能的影响对于编写高效的Python程序至关重要。开发者需要根据具体的应用场景选择合适的并发编程策略。 事件循环和回调机制的原理 在asyncio中事件循环Event Loop是负责协调和调度所有协程任务的核心组件。它通过轮询polling或其他机制来监视各种I/O操作和定时器并在这些操作准备就绪时执行相应的回调函数。事件循环的存在使得asyncio能够在单个线程中实现非阻塞的并发执行。 协程与GIL的关系 协程Coroutine是一种用户态的轻量级线程它们可以在单个线程中被挂起和恢复。由于协程的切换不涉及操作系统层面的上下文切换因此它们的开销远小于系统线程。在asyncio中协程通过async和await关键字定义和控制这些关键字允许开发者编写出看起来像同步代码的异步代码。 如何避免GIL竞争 由于GIL的存在多线程Python程序在执行Python字节码时会遇到性能瓶颈。然而asyncio中的协程并不直接执行Python字节码而是在事件循环的控制下调度。当协程执行到await表达式时它会挂起当前协程的执行并将控制权交给事件循环以便执行其他任务。这个挂起过程不涉及GIL的释放和重新获取因为协程的执行是在用户态控制的而不是在解释器的字节码级别。 异步编程的优势 通过使用事件循环和协程asyncio能够在单线程中高效地处理大量并发的I/O操作。这是因为在等待I/O操作完成时协程可以被挂起而事件循环可以转而执行其他协程。这种模式特别适合网络编程和服务器端应用其中通常有大量的等待时间如等待网络响应但CPU计算量相对较少。 总结来说asyncio通过事件循环和协程提供了一种避免GIL竞争的并发编程模型这种模型适用于I/O密集型任务能够在单线程中实现高效的并发执行。 asyncio asyncio 是 Python 标准库中的一个库用于编写并发代码特别是异步 I/O 操作。尽管 asyncio 本身并不绕过全局解释器锁GIL但它通过协程和事件循环的设计实现了高效的并发执行。 协程的工作原理 协程是一种用户态的轻量级线程它们可以在单个线程中交替执行。在 asyncio 中协程通过 async 关键字定义并使用 await 关键字在需要等待 I/O 操作完成时挂起执行。当协程挂起时它让出控制权给事件循环事件循环可以切换到其他协程执行从而实现并发。 事件循环的角色 事件循环是 asyncio 的核心组件它负责调度协程的执行。事件循环维护一个任务队列并在合适的时机执行这些任务。当协程执行到 await 表达式时它会将自己挂起并告诉事件循环去执行队列中的下一个任务。这样即使在单线程中也可以模拟多任务并发执行的效果。 异步 I/O 的实现 asyncio 利用了操作系统提供的非阻塞 I/O 接口。当协程执行 I/O 操作时它不会阻塞线程而是立即返回控制权给事件循环。事件循环可以在等待 I/O 操作完成的同时执行其他协程。一旦 I/O 操作准备就绪操作系统会通知事件循环事件循环随后唤醒相应的协程继续执行。 线程池的辅助作用 对于那些没有原生异步接口的阻塞 I/O 操作asyncio 提供了 run_in_executor 方法它可以将这些操作委托给线程池执行。这样即使是阻塞的 I/O 操作也不会阻碍事件循环的进度从而实现了异步执行。 结论 asyncio 通过协程和事件循环的协同工作以及对非阻塞 I/O 和线程池的利用实现了在单线程中的高效并发。尽管它不能消除 GIL 的限制但在 I/O 密集型应用中asyncio 能够显著提高程序的响应性和吞吐量。对于 CPU 密集型任务如果需要利用多核处理器的优势通常需要采用多进程或多线程的方法来绕过 GIL 的限制. await与协程执行 在asyncio中await关键字用于等待一个协程或异步操作的完成。然而await表达式并不会阻塞整个程序的执行而是只挂起当前的协程允许事件循环调度其他可执行的协程。这意味着在await表达式下的代码不会立即执行但程序中其他不依赖于等待结果的部分可以继续运行。 示例 假设我们有三个异步函数func1、func2和func3。下面的程序展示了如何使用await来安排它们的执行以及事件循环如何在等待期间调度其他任务。 import asyncioasync def func1():print(Starting func1)await asyncio.sleep(2) # 模拟耗时2秒的操作print(func1 finished)async def func2():print(Starting func2)await asyncio.sleep(1) # 模拟耗时1秒的操作print(func2 finished)async def main():print(Starting main)task1 asyncio.create_task(func1())task2 asyncio.create_task(func2())# 等待func1和func2的完成await task1await task2print(main finished)# 运行事件循环asyncio.run(main())在这个例子中main协程开始时首先创建并启动func1和func2任务。然后await task1和await task2将挂起main协程直到func1和func2分别完成。但重要的是在await表达式中事件循环可以调度并执行其他任务。 输出 Starting main Starting func1 Starting func2 func2 finished func1 finished main finished 从输出中可以看出func2在func1完成之前就完成了这是因为func2的等待时间比func1短。事件循环在等待func1和func2时调度了它们允许func2在func1完成前先完成。这展示了await如何只挂起当前协程而不会阻止事件循环调度其他任务。 这个例子展示了await关键字如何在不阻塞程序其他部分的情况下安排协程的执行。 Python中的concurrent模块 在Python中concurrent通常指的是concurrent.futures模块它提供了一个高层次的接口来执行异步任务无论是通过线程还是进程。concurrent.futures模块旨在简化并发编程使得开发者可以更容易地编写并发代码而不必担心底层的线程或进程管理细节。 concurrent.futures模块的使用 concurrent.futures模块提供了两种执行器ExecutorThreadPoolExecutor和ProcessPoolExecutor。ThreadPoolExecutor使用线程来执行任务适合I/O密集型任务因为它可以利用多个线程同时等待I/O操作完成。ProcessPoolExecutor使用进程来执行任务适合CPU密集型任务因为它可以利用多核处理器的计算能力并且不受全局解释器锁GIL的限制。 使用concurrent.futures模块时你可以提交任务给执行器并获取一个Future对象该对象表示异步操作的未来结果。你可以查询Future对象的状态等待任务完成或者在任务完成时获取结果。 threading模块的使用 相比之下threading模块提供了更低级别的线程管理。它允许你创建和管理线程提供了同步原语如锁、事件、条件变量和信号量来帮助管理多线程之间的资源共享和同步问题。threading模块适合那些需要精细控制线程行为的场景。 concurrent与threading的区别 1.抽象级别concurrent.futures提供了一个更高层次的抽象使得并发编程更加简洁。threading提供了更接近操作系统线程的直接控制。 2.易用性concurrent.futures通常更容易使用特别是对于不熟悉多线程编程细节的开发者来说。threading需要开发者手动管理线程生命周期和同步机制。 3.功能集concurrent.futures内置了任务队列和自动的线程管理而threading需要开发者自己实现这些功能。 4.适用场景concurrent.futures适合大多数并发编程任务尤其是当任务可以被分解为独立的工作单位时。threading适合需要精细控制线程行为的复杂应用。 在实际编程中选择使用concurrent.futures还是threading取决于具体的应用场景和开发者的偏好。如果需要快速实现并发任务且不需要深入控制线程行为concurrent.futures是一个更好的选择。如果项目需要精细的线程同步和管理或者是在Python 2环境中工作可能会更倾向于使用threading模块. Python中使用threading模块的例子 在Python中threading模块允许您创建和管理线程。以下是一个使用threading模块的简单例子它演示了如何创建一个线程来执行一个简单的任务 import threadingdef worker_function(name):线程要执行的函数print(fThread {name}: starting)# 模拟一些工作time.sleep(2) # 假设每个任务耗时2秒print(fThread {name}: finishing)# 创建线程thread threading.Thread(targetworker_function, args(Worker,))# 启动线程thread.start()# 等待线程完成thread.join()print(Main thread finished) 在这个例子中我们定义了一个worker_function它将被线程执行。我们创建了一个Thread对象并将worker_function作为目标传递给它。然后我们调用start()方法来启动线程并使用join()方法等待线程完成。 Python中使用concurrent.futures模块的例子 concurrent.futures模块提供了一个更高层次的接口来执行异步任务。以下是使用ThreadPoolExecutor的例子它展示了如何提交多个任务到线程池并等待它们完成 import concurrent.futuresimport timedef task(name):线程要执行的函数print(fTask {name}: starting)# 模拟一些工作time.sleep(2) # 假设每个任务耗时2秒print(fTask {name}: finishing)return fTask {name} completed# 创建线程池with concurrent.futures.ThreadPoolExecutor(max_workers3) as executor:# 提交任务到线程池future_to_name {executor.submit(task, name): name for name in [A, B, C]}# 获取完成的任务for future in concurrent.futures.as_completed(future_to_name):name future_to_name[future]print(fResult of Task {name}: {future.result()})print(All tasks completed) 在这个例子中我们定义了一个task函数它将被多个线程执行。我们使用ThreadPoolExecutor创建了一个线程池并提交了三个任务。通过as_completed函数我们可以迭代完成的任务并获取它们的结果。这种方式可以让您更方便地处理并发任务而无需直接管理线程的创建和销毁。 以上代码示例展示了如何在Python中使用threading和concurrent.futures模块来实现多线程编程。threading提供了更底层的控制而concurrent.futures提供了更高级别的抽象使得并发编程更加直观和易于管理。 四、python的列表和元组不同 在Python中元组tuple和列表list是两种基本的数据结构它们都可以存储一系列的项目但它们在可变性、语法、性能和使用场景上有所不同。 以下是元组和列表的对比表格 对比维度元组tuple列表list可变性不可变一旦创建就不能修改可变可以修改、添加或删除元素语法使用圆括号 () 定义使用方括号 [] 定义性能通常在执行速度上比列表快尤其是在遍历和访问时由于可变性列表在修改操作时可能较慢功能较少的方法如 count() 和 index()更多的内置方法如 append(), remove(), pop() 等用途用于描述一个不会改变的事务的多个属性通常用于存储内容可以随时间更改的项大小通常比列表内存效率高因为它们不可变存储空间可能略大于元组因为列表需要存储额外的指针和长度信息安全性由于不可变它们通常比列表更安全不容易被意外修改可变性可能导致数据被意外修改的风险 根据上述对比选择使用元组还是列表取决于具体的使用场景。如果需要一个可以修改的集合应该选择列表如果需要一个不可变的集合或者在函数间传递数据以确保数据的完整性应该选择元组。在性能敏感的应用中元组可能是更好的选择因为它们在内存使用和创建时间上通常更加高效。 五、上下文管理器 在Python中“上下文”这个概念通常与“上下文管理器”相关联主要通过with语句来体现。上下文管理器定义了一组操作这些操作在代码块执行前后自动调用从而提供了对资源如文件、网络连接、锁等的优雅管理确保资源被正确地打开和关闭。 上下文管理器协议 上下文管理器遵守以下协议 1.enter() 方法当with语句开始执行时enter()方法被调用。它通常用于设置或初始化资源如打开文件、获取锁等并返回一个值通常为管理器自身这个值可以被with语句中赋值给的变量使用。 2.exit() 方法当with语句块中的代码执行完毕或者在执行过程中遇到异常时exit()方法被调用。它负责释放或清理资源如关闭文件、释放锁等。 示例代码 下面是一个使用上下文管理器的简单示例用于安全地打开和关闭文件 class ManagedFile:def __init__(self, filename):self.filename filenamedef __enter__(self):self.file open(self.filename, r)return self.filedef __exit__(self, exc_type, exc_val, exc_tb):if self.file:self.file.close()# 使用with语句with ManagedFile(example.txt) as f:for line in f:print(line) 在这个例子中ManagedFile类实现了上下文管理器协议。在with语句中enter()方法打开文件并返回文件对象而__exit__()方法在代码块执行完毕后关闭文件。 通过with语句即使在处理文件时发生异常exit()方法也会被调用从而确保文件被正确关闭这使得资源管理变得更加安全和高效。 上下文管理器的使用不仅限于文件操作它同样适用于数据库连接、网络连接、锁等需要精细控制和安全清理的资源管理场景。 六、递归锁的概念 递归锁Recursive Lock是一种特殊类型的锁它允许同一个线程多次获得同一把锁。在多线程编程中递归锁特别有用因为它可以解决在递归函数或嵌套代码块中的锁定问题。当一个线程已经获得了锁并且在持有锁的代码块中再次尝试获取同一把锁时递归锁可以让该线程继续获取锁而不会导致死锁。递归锁通常通过内部计数器来跟踪锁的获取次数确保只有当计数器归零时锁才会真正释放其他线程才能获取该锁. 递归锁的工作原理 当一个线程首次请求递归锁时如果锁是未上锁状态线程会获得锁并将锁的状态设置为已上锁同时将上锁次数设置为1。如果同一个线程再次请求上锁递归锁会检查当前线程是否已经持有该锁如果是则增加上锁次数锁保持上锁状态。每次线程释放锁时上锁次数减1。只有当上锁次数减到0时锁才会完全释放其他线程才能获取该锁. 递归锁的应用场景 递归锁适用于以下场景 1.递归函数或方法当一个递归函数或方法需要在每一次递归调用时获取同一个锁时递归锁可以保证线程不会因为获取同一个锁而产生死锁。 1.嵌套的临界区当一个线程在一个临界区内部再次进入同一个临界区时递归锁可以确保线程不会因为自己已经持有锁而被阻塞. 使用递归锁时的注意事项 尽管递归锁提供了便利但在使用时仍需注意避免过度的锁嵌套以免引起性能问题或潜在的死锁风险。每次上锁操作都必须对应相同次数的解锁操作否则可能导致死锁或其他同步问题. 在不同的编程语言中递归锁的实现和术语可能有所不同。例如在Python中递归锁可以通过threading.RLock来实现而在Java中可以使用java.util.concurrent.locks.ReentrantLock来实现递归锁的功能. 七、Python的销毁机制 Python的销毁机制涉及对象的生命周期管理主要包括引用计数和垃圾回收两个方面。 引用计数 Python使用引用计数来跟踪对象的生命周期。每个对象都有一个引用计数器当一个对象被创建时其引用计数被初始化为1。每当对象的引用被创建或增加时引用计数器递增当引用被销毁或减少时引用计数器递减。当一个对象的引用计数降至0时表示没有任何引用指向该对象对象就会被销毁其占用的内存随后可以被回收。 垃圾回收 尽管引用计数机制非常高效但它无法处理循环引用的问题。循环引用发生在两个或更多的对象相互引用形成一个封闭的引用链即使外部没有引用这些对象它们的引用计数也不会降到0。为了解决这个问题Python引入了垃圾回收机制包括标记-清除算法和分代回收算法。 1.标记-清除算法该算法分为标记和清除两个阶段。首先垃圾回收器标记所有可达的对象即从根对象开始通过引用链可达的对象然后清除所有未标记的对象。根对象通常是全局变量、调用栈和寄存器中的对象。 2.分代回收算法分代回收算法是建立在标记-清除算法基础上的它利用了这样一个观察大多数对象的生命周期很短而少数对象的生命周期很长。Python将内存分为不同的代年轻代包含新生成的对象中年代和老年代包含存活时间较长的对象。垃圾回收器根据对象的年龄和存活时间来决定何时以及如何进行垃圾回收以提高效率。 __del__方法 在Python中可以通过定义__del__方法来执行对象销毁前的清理工作。当对象的引用计数降至0时如果对象定义了__del__方法该方法将被调用。然而__del__方法的调用时机是不确定的因为垃圾回收是由Python的垃圾回收器在后台管理的。因此依赖__del__方法来执行关键的清理工作可能不是最佳实践。 注意事项 1.在编写Python代码时通常不需要手动管理内存因为Python的垃圾回收机制会自动处理对象的销毁。 2.应该避免在__del__方法中执行耗时的操作因为这可能会影响程序的响应性。 3.应该谨慎使用__del__方法优先考虑使用上下文管理器with语句或其他资源管理策略来确保资源被及时释放。 Python的销毁机制旨在简化内存管理减少内存泄漏的风险并提供足够的灵活性来处理复杂的资源管理需求。 八、Python中的进程通信 在Python中两个进程可以通过多种方式进行通信。以下是一些常见的进程间通信IPC方法 使用multiprocessing模块的Queue multiprocessing模块提供了一个线程安全的Queue类可以在多个进程之间传递数据。使用Queue可以很容易地实现进程间的数据同步和通信。 from multiprocessing import Process, Queuedef worker(queue):queue.put(Hello from worker)if __name__ __main__:queue Queue()p Process(targetworker, args(queue,))p.start()p.join()print(queue.get())使用multiprocessing模块的Pipe Pipe提供了一种基于管道的通信方式适合两个进程之间的通信。管道可以是单向的或双向的允许进程发送和解析数据。 from multiprocessing import Process, Pipedef sender(conn):conn.send(Hello from sender)def receiver(conn):print(conn.recv())if __name__ __main__:parent_conn, child_conn Pipe()p Process(targetsender, args(parent_conn,))r Process(targetreceiver, args(child_conn,))p.start()r.start()p.join()r.join()使用multiprocessing模块的shared_memory 共享内存是一种高效的进程间通信方式允许多个进程访问同一块内存。这要求程序员仔细管理内存访问以防止竞态条件。 from multiprocessing import Value, shared_memorydef writer(shm):shm.value 1def reader(shm):print(shm.value)if __name__ __main__:sm shared_memory.SharedMemory(createTrue, sizeint(1e9))sm_object Value(i, 0, lockFalse)sm_object.attach(sm)pw Process(targetwriter, args(sm_object,))pr Process(targetreader, args(sm_object,))pw.start()pr.start()pw.join()pr.join()使用multiprocessing模块的Manager Manager提供了一种基于客户端-服务器模型的进程间通信方式可以创建代理对象如字典、列表等这些对象可以在多个进程之间共享。 from multiprocessing import Process, Managerdef update(d, lst):d[1] updatedlst.append(2)if __name__ __main__:with Manager() as manager:d manager.dict()lst manager.list(range(3))p Process(targetupdate, args(d, lst))p.start()p.join()print(d) # {1: updated}print(lst) # [0, 1, 2, 2]以上方法各有优缺点适用于不同的场景。在选择进程间通信机制时您应该考虑数据的类型、通信的复杂性、性能要求以及代码的可维护性。
文章转载自:
http://www.morning.zxgzp.cn.gov.cn.zxgzp.cn
http://www.morning.mslsn.cn.gov.cn.mslsn.cn
http://www.morning.nhlyl.cn.gov.cn.nhlyl.cn
http://www.morning.kxymr.cn.gov.cn.kxymr.cn
http://www.morning.dhmll.cn.gov.cn.dhmll.cn
http://www.morning.bssjz.cn.gov.cn.bssjz.cn
http://www.morning.jhrkm.cn.gov.cn.jhrkm.cn
http://www.morning.mslsn.cn.gov.cn.mslsn.cn
http://www.morning.yhtnr.cn.gov.cn.yhtnr.cn
http://www.morning.lthgy.cn.gov.cn.lthgy.cn
http://www.morning.tbkqs.cn.gov.cn.tbkqs.cn
http://www.morning.kbqqn.cn.gov.cn.kbqqn.cn
http://www.morning.brld.cn.gov.cn.brld.cn
http://www.morning.zrlwl.cn.gov.cn.zrlwl.cn
http://www.morning.rjcqb.cn.gov.cn.rjcqb.cn
http://www.morning.pnljy.cn.gov.cn.pnljy.cn
http://www.morning.ghxtk.cn.gov.cn.ghxtk.cn
http://www.morning.jncxr.cn.gov.cn.jncxr.cn
http://www.morning.ccpnz.cn.gov.cn.ccpnz.cn
http://www.morning.mjctt.cn.gov.cn.mjctt.cn
http://www.morning.tfpqd.cn.gov.cn.tfpqd.cn
http://www.morning.nfbkz.cn.gov.cn.nfbkz.cn
http://www.morning.tgfjm.cn.gov.cn.tgfjm.cn
http://www.morning.flmxl.cn.gov.cn.flmxl.cn
http://www.morning.rkxqh.cn.gov.cn.rkxqh.cn
http://www.morning.smdiaosu.com.gov.cn.smdiaosu.com
http://www.morning.gzzncl.cn.gov.cn.gzzncl.cn
http://www.morning.kwblwbl.cn.gov.cn.kwblwbl.cn
http://www.morning.lthgy.cn.gov.cn.lthgy.cn
http://www.morning.chtnr.cn.gov.cn.chtnr.cn
http://www.morning.mdmc.cn.gov.cn.mdmc.cn
http://www.morning.cklld.cn.gov.cn.cklld.cn
http://www.morning.lcxdm.cn.gov.cn.lcxdm.cn
http://www.morning.lsnhs.cn.gov.cn.lsnhs.cn
http://www.morning.qbnfc.cn.gov.cn.qbnfc.cn
http://www.morning.fbhmn.cn.gov.cn.fbhmn.cn
http://www.morning.ryxyz.cn.gov.cn.ryxyz.cn
http://www.morning.jrqw.cn.gov.cn.jrqw.cn
http://www.morning.gnjtg.cn.gov.cn.gnjtg.cn
http://www.morning.pttrs.cn.gov.cn.pttrs.cn
http://www.morning.yxmcx.cn.gov.cn.yxmcx.cn
http://www.morning.txqgd.cn.gov.cn.txqgd.cn
http://www.morning.tnmmp.cn.gov.cn.tnmmp.cn
http://www.morning.mprky.cn.gov.cn.mprky.cn
http://www.morning.fndfn.cn.gov.cn.fndfn.cn
http://www.morning.twpq.cn.gov.cn.twpq.cn
http://www.morning.kfcz.cn.gov.cn.kfcz.cn
http://www.morning.nhzzn.cn.gov.cn.nhzzn.cn
http://www.morning.hxxyp.cn.gov.cn.hxxyp.cn
http://www.morning.mjzgg.cn.gov.cn.mjzgg.cn
http://www.morning.jzlfq.cn.gov.cn.jzlfq.cn
http://www.morning.mwmtk.cn.gov.cn.mwmtk.cn
http://www.morning.hcbky.cn.gov.cn.hcbky.cn
http://www.morning.lqgtx.cn.gov.cn.lqgtx.cn
http://www.morning.qxrct.cn.gov.cn.qxrct.cn
http://www.morning.dnphd.cn.gov.cn.dnphd.cn
http://www.morning.bzfld.cn.gov.cn.bzfld.cn
http://www.morning.xqtqm.cn.gov.cn.xqtqm.cn
http://www.morning.pqnkg.cn.gov.cn.pqnkg.cn
http://www.morning.jghty.cn.gov.cn.jghty.cn
http://www.morning.ggtkk.cn.gov.cn.ggtkk.cn
http://www.morning.lgcqj.cn.gov.cn.lgcqj.cn
http://www.morning.gkktj.cn.gov.cn.gkktj.cn
http://www.morning.mypxm.com.gov.cn.mypxm.com
http://www.morning.thwcg.cn.gov.cn.thwcg.cn
http://www.morning.rbkl.cn.gov.cn.rbkl.cn
http://www.morning.zwznz.cn.gov.cn.zwznz.cn
http://www.morning.jwrcz.cn.gov.cn.jwrcz.cn
http://www.morning.pzss.cn.gov.cn.pzss.cn
http://www.morning.xnpml.cn.gov.cn.xnpml.cn
http://www.morning.sgbjh.cn.gov.cn.sgbjh.cn
http://www.morning.gyylt.cn.gov.cn.gyylt.cn
http://www.morning.bbtn.cn.gov.cn.bbtn.cn
http://www.morning.cljmx.cn.gov.cn.cljmx.cn
http://www.morning.tcsdlbt.cn.gov.cn.tcsdlbt.cn
http://www.morning.hcsqznn.cn.gov.cn.hcsqznn.cn
http://www.morning.jsljr.cn.gov.cn.jsljr.cn
http://www.morning.ltypx.cn.gov.cn.ltypx.cn
http://www.morning.hyryq.cn.gov.cn.hyryq.cn
http://www.morning.nkyc.cn.gov.cn.nkyc.cn
http://www.tj-hxxt.cn/news/273975.html

相关文章:

  • 伪原创对网站的影响广州app定制公司
  • 做兼职编辑的网站网站建设基本流程费用
  • 网站建设注意哪些方面比较出名做耐克的网站
  • 网站建设接单企业邮箱是qq邮箱吗
  • 影院网站模板电商代运营公司100强
  • 网站建设自学网找人做网站要多少钱
  • wordpress调用分类链接南京seo新浪
  • 营销型 展示类网站模板wordpress通栏图片插件
  • 做外贸常用那几个网站中国菲律宾足球历史战绩
  • 电商网站订货网站建设方法叁金手指下拉丶
  • 任何用c语言做网站兰州网站制作公司服务电话
  • 开创云网站建设怎么用动图做网站背景
  • 微信公众好第三方网站怎么做淘宝联盟登记新网站
  • 网站建设公众号小程序开发网站域名解析ip
  • 建设公司网站需要多少天宝丰网站制作公司
  • 谷歌做网站现在建一个网站一年费用只要几百元
  • 摄影作品出售网站wordpress试试手气
  • 商城类网站方案有经验的网站建设
  • 外贸网站建设注意事项网站定制与模板开发
  • 一加官方网站进入视觉设计专业就业前景
  • 成都电子商务网站开发成都网络营销公司哪家好
  • 个人网站介绍足球比赛直播在线观看
  • 网站建设入门培训十大社区团购平台有哪些
  • 做音乐 交流网站网站备案的幕布是什么来的
  • 经营网站备案多语言网站(如何实现网站的多语言版本 )
  • 0基础学做网站宝安龙华积分商城网站建设
  • wordpress导航站模版网站与微信对接
  • 高碑店网站建设hexo 导入 wordpress
  • 青岛正规网站设计公司各网站文风
  • 沈阳建站平台网络设计课程设计前言