为了账号安全,请及时绑定邮箱和手机立即绑定

10 种方法写出更好的 Python 代码

标签:
Python

您需要了解的每日小技巧和窍门!

Python Block — (Image by Author)

(图片由作者提供)

我们已经接近2024年的一半,由于生成式AI和大型语言模型的兴起,科技行业的发展速度比以往任何时候都要快。

额外内容: 了解生成式AI背后的技术趋势!,这里。👈🏻

作为一名数据爱好者和Python开发者,你应该了解这些酷炫的技巧和窍门,以保持在当今竞争激烈的市场中的相关性,因为在这样的市场中,成千上万的科技人员被裁员。

首先要注意的是,

为什么选择 Python?

Python 是一种极其强大的通用编程语言,非常适合数据科学相关任务——利用其广泛的库和出色的框架,如 SciKit-Learn、TensorFlow、PyTorch 等,由于其易于理解的语法。

这意味着掌握 Python 可以为你提供灵活性,可以创建各种应用程序,例如 自动化任务构建聊天机器人与开源大型语言模型互动,特别是对于初学者来说。与其他编程语言相比,Python 相对简单且学习速度快。

所以,说了这么多,让我们开始编码,开始探索吧,

如何写出更好的Python代码?

我将讨论 10个技巧,这些技巧一定能 提升和改进你的编码实践

如果你觉得这些小贴士有帮助,不要忘了点赞👏🏻 并在下方留言写下你的想法!

1. 使用“生成器”节省内存

假设你需要分析一个 large_file,这个文件 无法一次性装入你的内存中。为了高效地解决这个问题,你可以创建一个 process_large_file 生成器函数,逐行读取大文件

生成器不仅有助于将大数据分批次处理,而且在内存使用上也更高效,因为你不需要将整个数据存储在内存中。

    def process_large_file(file_path):  
        """  
        生成器函数,用于逐行读取大文件。  
        """  
        with open(file_path, 'r') as file:  
            for line in file:  
                # 处理每一行  
                print(line)  
      
    # 使用生成器处理日志文件  
    log_file_path = 'path/to/your/large_file.txt'  
      
    # 处理大文件  
    process_large_file(file_path)

上述代码显示了你 large_file.txt 文件中的内容。

甚至Keras框架也使用生成器来并行加载和处理批量数据,以减少训练时间。

2. 在字典中使用 “.setdefault()”

假设你正在 管理一个库存系统 ,并且希望跟踪各种产品的库存水平。当一个新的产品被添加到系统中时, 确保它还没有设置库存水平的话,默认设置一个库存水平

你可以使用 setdefault() 函数来简化这个过程,如果键在字典中不存在,则插入带有指定默认值的键。

    # 初始库存  
    inventory: dict[str, int] = {"jeans": 500, "top": 600}       
                    
    # 如果不存在,则添加更多产品并设置默认库存量  
    products_to_add: list[str] = ["skirt", "shirt", "tee"]  
      
    for product in products_to_add:  
        inventory.setdefault(product, 500)  
      
    # 打印最终更新后的库存  
    print("最终更新后的库存:", inventory)  
      
    """  
    # 输出:  
    最终更新后的库存: {'jeans': 500, 'top': 600, 'skirt': 500, 'shirt': 500, 'tee': 500}  
    """

这样可以避免显式的检查和赋值,使代码更加简洁和易读。

3. 使用字典避免“if-elif”地狱

假设你有几个函数,你想根据用户输入来调用它们。

好吧,解决这个问题最常见的方式是使用 if-elif 条件,但这种方法在处理成百上千个函数时可能会变得非常冗长和复杂。

另一种方法是 创建一个字典 ,该字典包含键——你想要检查的函数作为值。

    from collections.abc import Callable  
      
    # 函数 1   
    def first():  
      print("调用第一个函数...")  
      
      
    # 函数 2  
    def second():  
      print("调用第二个函数...")  
      
      
    # 函数 3  
    def third():  
      print("调用第三个函数...")  
      
      
    # 默认函数  
    def default():  
      print("调用默认函数...")  
      
    # 用户输入  
    options: int = int(input("输入一个选项:", ))  
      
    # 用于保存选项作为键和函数作为值的字典    
    funcs_dict : dict[int, Callable[[], None]] = {1: first, 2: second, 3: third}  
      
    # 检查键是否存在,如果不存在则运行默认函数  
    final_result = funcs_dict.get(options, default)  
    final_result()

当你运行程序时,你可以在屏幕上看到以下结果。

    """  
    # 输出:  
    # 当选项为 0 时  
    输入选项:0  
    调用默认函数...  
      
    # 当选项为 1 时  
    输入选项:1  
    调用第一个函数...  
      
    # 以此类推...  
    """

注意:如果用户请求任何随机内容,将运行 default() 函数。

4. 使用“Counter”模块中的“Counter”

相信我,在处理大量文本数据时,文本分析中最常见的任务包括识别关键术语,为此你需要确定每个单词在特定文档或整个语料库中的出现频率,这取决于具体的问题需求。

Counter 提供了一种简单而高效的方法来统计可迭代对象中的元素,抽象了编写自定义计数逻辑的复杂性。

让我们实现这个功能,

    from collections import Counter  
    import re  
      
    # 读取文本文件  
    with open("sample_text.txt", "r") as file:  
      text = file.read()  
      
    # 清洗和分词  
    cleaned_text: str = re.sub(r'[^\w\s]', '', text.lower().split())  
      
    # 使用 Counter() 统计单词数量  
    word_counts: Counter = Counter(cleaned_text)  
      
    # 打印第二高频的单词  
    most_common = word_counts.most_common(2)  # 传入的数字表示我们想要多少个最常见的单词(从1-n开始计数)  
    print("第二高频的单词是: ", most_common[1])  # 从最常见的两个单词中打印第二个单词  
      
    """  
    # 输出:  
    第二高频的单词是: ('data', 82)  
    """  
    

注意:此外,您还可以执行算术运算,轻松将 Counter 对象转换为其他数据结构(如字典),并利用其有用的方法,如 element()most_common() 等。

5. 使用“记忆化”来优化代码

Memoization 是动态规划中用于 提高递归算法的时间复杂度 的一种技术,通过在相同输入再次出现时重用昂贵的函数调用来实现。

一个经典的例子是兔子问题,也被称为斐波那契数列

    import time  
      
    def memo_fibonacci(num: int, dictionary: dict[int, int]):  
        if num in dictionary:  
            return dictionary[num]  
        else:  
            dictionary[num] = memo_fibonacci(num-1, dictionary) + memo_fibonacci(num-2, dictionary)  
            return dictionary[num]  
      
    # 使用字典进行缓存  
    dictionary: dict[int, int] = {0: 1, 1: 1}  
      
    # 记录开始时间  
    start_time: float = time.time()  
    # 调用函数  
    result: int = memo_fibonacci(48, dictionary)  
    end_time: float = time.time()  
    # 计算耗时  
    elapsed_time: float = end_time - start_time  
      
      
    print(f"结果: {result}") # 结果: 7778742049  
    print(f"耗时: {elapsed_time:.6f} 秒") # 耗时: 0.000133 秒

注意:这确实显著降低了时间复杂度。但请记住,这伴随着时间空间的权衡,因为你需要维护一个缓存来存储结果,这需要你进行管理。

6. 使用“@装饰器”来避免重复性

假设你在构建一个Python项目,并希望测量**某个函数运行所需的时间。当然,你可以使用time功能来实现这一点,但如果你有数十个甚至上百个函数**呢?

这将会花费很长时间来编写“开始时间”和“结束时间”,相反,我们可以创建一个函数 elapsed_time 来为我们完成相同的工作。我们只需要在想要计时的函数上添加 @elapsed_time 即可。

什么是**@装饰器**

装饰器是 Python 的一种独特功能,可以在你的现有函数周围包裹一层,允许你在函数执行之前或之后修改或增强函数,而不改变核心逻辑。

Python 看到 @ 符号,就知道下面的函数需要传递给一个名为 elapsed_time 的函数,然后该函数会在 elapsed_time 中运行,并用额外的代码将其包裹起来,以计算任何函数的运行时间。

    import time  
      
    def elapsed_time(func):  
        def wrapper():  
            start_time: float = time.time()  
              
            func()  
              
            end_time: float = time.time() - start_time  
            print(f"{func.__name__}() took {end_time:.6f} seconds")  
              
        return wrapper  
      
    @elapsed_time  
    def some_code():  
        # 模拟运行代码...  
        time.sleep(0.00002)  
      
    # 调用函数  
    some_code() # some_code() took 0.000009 seconds

它们广泛用于**日志记录、计时、强制访问控制**等。

注意:不过,还是建议不要过度使用,因为它们也可能使你的代码实际在做什么变得模糊。

7. 使用 dataclass 实现干净的数据结构

在只用于存储数据值的常规类中,反复编写 __init__ 方法真的非常繁琐,这可能会导致潜在的错误。

(图片由作者提供)

然而,在 Python 3.7 中引入的 dataclasses 模块是一种更高效的方式来存储将在程序的不同部分之间传递的数据。

注意,只需几行代码,我们就可以创建更少错误的数据类,而无需手动编写构造函数和其他已经实现的方法。

    from dataclasses import dataclass  
      
    @dataclass  
    class 员工:  
        id_: int  
        名称: str  
        工资: float  
      
    e1 = 员工(id_=1, 名称='Tusk', 工资=69999.99)  
    print(e1) # 员工(id_=1, 名称='Tusk', 工资=69999.99)

在这里,输出也等同于使用 __repr__ 实现的标准 Python 类。

**注意 : **我们也可以自定义 Employee 类的表示形式:

    from dataclasses import dataclass  
      
    @dataclass  
    class Employee:  
        id_: int  
        name: str  
        salary: float  
      
        def __repr__(self):  
            return f"Employee(id_={self.id_}, name={self.name}, salary={self.salary})"  
      
        def __str__(self):  
           return f"{self.name} 获得 ${self.salary}."  
      
    e1 = Employee(id_=1, name='Tusk', salary=69999.99)  
    print(repr(e1))  # Employee(id_=1, name=Tusk, salary=69999.99)  
    print(str(e1))   # Tusk 获得 $69999.99.

注意

__repr__ 方法提供了 Employee 对象的明确表示。

__str__ 方法提供了 Employee 对象更易读和简洁的描述。

如果你还没有使用 dataclass,那就开始使用它吧,以减少样板代码,使你的代码更易读和更易维护。

8. 使用“match”进行干净的输入处理

自从 Python 3.10 开始,结构化模式匹配作为 match 模式和相关的 case 语句被添加进来。

假设我们有一个名为“Point”的类,它表示二维坐标系统中的一个点。现在,我们将创建一个名为“where_is”的函数来处理用户输入以在二维平面上查找点的情况。

**match** 语句会取一个表达式,并将其值与后续的模式匹配块进行比较。

    from dataclasses import dataclass  
      
    # 使用 dataclass 定义一个类  
    @dataclass  
    class Point:  
        x: int  
        y: int  
      
    # 使用 match 语句处理不同的情况  
    def where_is(point):  
        match point:  
            case Point(x=0, y=0):  
                return ("原点")  
            case Point(x=0, y=y):  
                return (f"Y={y}")  
            case Point(x=x, y=0):  
                return (f"X={x}")  
            case Point(x, y):  
                return("其他地方")  
            # 捕获用户输入的其他任何内容  
            case _:  
                return("不是点")  
      
    # 示例  
    print(where_is(Point(0, 0)))   # 输出: 原点  
    print(where_is(Point(0, 10)))  # 输出: Y=10  
    print(where_is(Point(10, 0)))  # 输出: X=10  
    print(where_is(Point(10, 10)))  # 输出: 其他地方  
    print(where_is("Not a point"))  # 输出: 不是点

使用 match-case 语句,你可以处理所有可能的情况,确保 穷尽模式匹配

9(A). 使用 “all” 操作符代替 “and”

想象一下你在构建一个用户资料系统,并希望验证表单中的所有必填字段是否都已填写(我不知道为什么你不直接在表单中标记必填项,🤷🏻‍♀️不过我们还是集中精力在这里👇)。

当然,你可以使用 all 函数,它会在所有提供的可迭代对象中的元素都为 True 时返回 True,而不是使用 and 条件。

    # 来自注册表单的用户输入
    form_data: dict[str, str] = {"name": "Nikita",
                                 "email": "analyticalnikita@gmail.com",
                                 "phone": "123478911"}
      
    # 必填字段列表
    required_fields: list[str] = ["name", "email", "phone"]
      
    # 使用 all 操作符
    if all(field in form_data for field in required_fields):
        print("所有必填字段都已填写。")
    else:
        print("某些必填字段缺失或为空。")
      
    """  
    # 输出:
    所有必填字段都已填写。
    """

9(B). 使用 “any” 操作符 代替 “or”

any 函数如果可迭代对象中的任何元素为 True,则返回 True

例如,你需要根据某些条件限制某些用户的权限,这时你可以使用 any,而不是 or 条件。

    # 用户的权限列表  
    user_permission: list[str] = ["read", "execute"]  
      
    # 检查用户是否至少拥有其中一个所需的权限  
    required_permissions: list[str] = ["write", "read", "admin"]  
          
    # 使用 "any" 操作符  
    if any(permission in user_permission for permission in required_permissions):  
        print(f"由于你拥有所需的权限。访问被允许。")  
    else:  
        print("你是一个标准用户。不允许访问。")  
      
    """  
    # 输出:  
    由于你拥有所需的权限。访问被允许。  
    """

这些是一些示例,展示了如何使用 anyall 简化原本需要多个 orand 语句的条件。

最后但同样重要的是,每个程序员的终身必备的 (嘿,如果不是你,那就跟着我一起学习这些吧😉)

10. 使用列表推导式等简洁语法

理解是Python为所有可迭代数据类型提供的强大工具。这提供了一种简洁的方法,可以根据情况使用单行代码避免多行循环。

让我们一一探索它们:

10(A). 列表推导式

这里我使用了一个嵌套的if语句的例子,来展示列表推导式的力量

    # 使用列表推导式的嵌套if语句  
    fruits: list[str] = ["apple", "orange", "avacado", "kiwi", "banana"]  
    basket: list[str] = ["apple", "avacado", "apricot", "kiwi"]  
      
    [i for i in fruits if i in basket if i.startswith("a")] # ['apple', 'avacado']

同样,你也可以使用 嵌套的for循环 ,直到它让你的代码难以阅读为止。

10(B). 元组推导式

在 Python 中,元组推导式是不存在的。相反,你可以使用 生成器表达式 来创建元组。

    # 生成器表达式转换为元组  
    tuple(i**2 for i in range(10))  
      
    # (0, 1, 4, 9, 16, 25, 36, 49, 64, 81)

10©. 字典推导式

假设你有一个列表 apple_names,你想打印出一个新的列表,这个新列表包含 apple_names 中每个元素的长度。

当然,你可以在这里使用 列表推导。但是你知道吗,你也可以使用这种语法来创建一个字典,这被称为 字典推导

    # 创建一个苹果名称的列表  
    apple_names: list[str] = ["apple", "pineapple", "green apple"]  
      
    # 使用苹果名称作为键,它们的长度作为值来创建一个字典  
    print({apple_name: len(apple_name) for apple_name in apple_names})  
      
    # {"apple": 5, "pineapple": 9, "green apple": 11} 

或者,它比使用循环或 dict 构造函数来创建字典更易读。

10(D). 集合推导式

你也可以在列表推导中基于某些条件进行过滤。

    # 创建一个满足条件的集合  
    print({i**2 for i in range(1, 11) if i > 5})  
      
    # {64, 36, 100, 49, 81}

注意:虽然列表推导式很简洁,但这并不意味着它们适用于所有情况,特别是涉及过于复杂的逻辑时。

结论

记住,你写的代码不是给计算机看的,而是给一起工作的团队看的。因此,编写更好的代码真的很重要,这样别人才能理解你的生产代码。

查找完整代码范围****这里 给 GitHub 仓库点个星⭐。

通过采用这些技巧,你不仅能够编写高效的代码,还能提高你的生产力。

此外,为了将你的编程技能提升到下一个水平,理解 Python 的 命名空间和作用域 以更好地优化你的代码这里。👈🏻

在你离开之前… 如果你有任何 问题/建议/想法 ,不妨在下面留言。✍️

并且,如果你喜欢这篇内容,请 鼓掌 50 👏,并且不要忘了关注 这里 获取未来的更新。

就这样了。不久再聊! 🙋🏻‍♀️

— Nikita Prasad

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消