Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wp-pagenavi domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114

Notice: 函数 _load_textdomain_just_in_time 的调用方法不正确twentyseventeen 域的翻译加载触发过早。这通常表示插件或主题中的某些代码运行过早。翻译应在 init 操作或之后加载。 请查阅调试 WordPress来获取更多信息。 (这个消息是在 6.7.0 版本添加的。) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114
高级编程 – 第 2 页 – 煅魂-JeffreyChu的修炼屋

简单的打印,还可以玩得这么花?

在 Python 编程中,print函数是最基础但却非常重要的工具之一。它不仅用于输出信息到控制台,还可以用于调试和展示程序的运行状态。

print 函数在python3里已经从python2关键字升级为了函数,这让它有了更丰富的用法和玩法,今天我们将深入探讨print函数的各种用法和参数设置。

常用玩法

最简单的

最简单的 print 函数调用只需要一个参数,通常是我们的第一个python程序的内容

print("Hello, world!")

这段代码将输出 Hello, world! 到控制台。

Hello, world!
多个参数

print 函数可以接受多个参数,并将它们以空格分隔的形式输出:

print("Hello", "world", 2024)

输出结果为:

Hello world 2024
自定义分隔符

通过sep参数,可以自定义多个参数之间的分隔符(默认情况下sep参数指定为空格符)。例如,指定使用逗号和空格作为分隔符:

print("Hello", "world", 2024, sep=", ")

输出结果为:

Hello, world, 2024
自定义结束符

默认情况下,print 函数输出内容后会自动添加一个换行符。可以通过 end 参数自定义结束符,例如使用空格代替换行符:

print("Hello, world", end=" ")
print("Welcome to Python!")

输出结果为:

Hello, world Welcome to Python!
输出到文件

print 函数的 file 参数允许我们将输出重定向到文件而不是控制台。例如,将内容写入一个文本文件:

with open("output.txt", "w") as file:
    print("Hello, world!", file=file)

这段代码会在当前目录下创建一个名为 output.txt 的文件,并在其中写入 Hello, world!

flush 参数

flush 参数控制输出缓冲区的刷新。如果设置为 Trueprint 函数会立即将内容输出到目标设备,而不是等待缓冲区满或程序结束时再输出:

import time

print("Loading", end="", flush=True)
for i in range(5):
    time.sleep(1)
    print(".", end="", flush=True)

输出结果为:

Loading.....

上面代码每隔一秒钟会打印一个点,是不是很像某个下载或者进度加载过程?

高级用法

格式化输出

使用 f-string(格式化字符串)来更灵活地格式化输出内容,这样输出的内容更加连贯(而不是像空格有割离感):

name = "Jeff"
age = 25
print(f"Name: {name}, Age: {age}")

输出结果为:

Name: Jeff, Age: 25

另外,Python3.8以上的版本甚至可以这样打印

name = "Jeff"
age = 25
print(f"{name=}, {age=}")

输出为:

name='Jeff', age=25
多行输出

可以使用三重引号(””” 或 ”’)来输出多行字符串:

print("""
Line 1
Line 2
Line 3
""")

输出结果为:

Line 1
Line 2
Line 3

花活玩法

带颜色字符串

使用 ANSI 转义序列可以在支持这些序列的终端中输出带颜色的文本。以下是一些常见的 ANSI 转义序列:

前景色:

  • 黑色: \033[30m
  • 红色: \033[31m
  • 绿色: \033[32m
  • 黄色: \033[33m
  • 蓝色: \033[34m
  • 紫色: \033[35m
  • 青色: \033[36m
  • 白色: \033[37m
    背景色:
  • 黑色: \033[40m
  • 红色: \033[41m
  • 绿色: \033[42m
  • 黄色: \033[43m
  • 蓝色: \033[44m
  • 紫色: \033[45m
  • 青色: \033[46m
  • 白色: \033[47m
    重置颜色:
  • \033[0m

例如:

print("\033[32m这是绿文本\033[0m")
print("\033[33m这是黄色文本\033[0m")
print("\033[31m\033[47m这是红色白底文本\033[0m")

输出:

循环加载

上面已经有一个loading加载的例子了,但是有些时候,我们重复在某一行进行循环加载,尤其是模拟网络下载时(比如像npm install某些库时的命令行动态效果)。

因为涉及到同行循环,所以会用上退格符 \b,来回退覆盖已经输出的字符串。

import time

print("Loading", end="", flush=True)
for _ in range(5):
    for _ in range(3):
        time.sleep(1)
        print(".", end="", flush=True)
    time.sleep(1)
    print("\b\b\b   \b\b\b", end="", flush=True)

其实还有其他特殊字符(如\t\r等)可以和print进行联动

打印形状
打印爱心
print('\n'.join([''.join([('*' if ((x * 0.04) ** 2 + (y * 0.1) ** 2 - 1) ** 3 - (x * 0.04) ** 2 * (y * 0.1) ** 3 <= 0 else ' ') for x in range(-30, 30)]) for y in range(15, -15, -1)]))

效果图:

“佛祖保佑,永无BUG”,这个打印可以用在程序启动的时候,有buff加成,非常玄学。

注意,这里用的是raw stingr字符串,主要是避免里面的\符号被转义了。

print("""
                  _ooOoo_
                 o8888888o
                 88" . "88
                 (| -_- |)
                 O\  =  /O
              ____/`---'\____
            .'  \\|     |//  `.
           /  \\|||  :  |||//  \\
          /  _||||| -:- |||||-  \\
          |   | \\\  -  /// |   |
          | \_|  ''\---/''  |   |
          \  .-\__  `-`  ___/-. /
        ___`. .'  /--.--\  `. . __
     ."" '<  `.___\_<|>_/___.'  >'"".
    | | :  `- \`.;`\ _ /`;.`/ - ` : | |
    \  \ `-.   \_ __\ /__ _/   .-` /  /
=====`-.____`-.___\_____/___.-`____.-'======
                  `=---='

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
""")

输出:

打印logo

以打印“X秘书”为例

print("""
X         X         X      X                 X     X                          
 X       X       X X         X   X        XXXXXXXX  X 
   X   X           X       X    X            X   X
     X          XXXXXXX  X X   X  X      XXXXXXXXXXXX                        
   X   X           X    X  X X     X         X      X
 X       X       X X X     X    x            X    xXX    
X         X     X  X  X  X XXXXXX            X
"""
)

输出:

结论
print 函数是 Python 中最基本但却功能强大的工具之一。通过了解和掌握 print 的各种参数和用法,可以更高效地调试和展示程序的运行结果。

如果你还发现print有其他玩法,欢迎讨论交流

Py面试必问:说说魔术方法与应用

在中高级Python开发面试中,面试官为了考察面试者是否了解面向对象编程,会通过Python魔术方法相关概念来变向提问

引言

定义和基本概念

魔术方法(Magic Methods) 是 Python 中一种特殊的函数,它们用于定义类的特定行为和属性。这些方法通常由 Python 解释器自动调用,而不是显式地在代码中调用。

它们允许你自定义类与内置操作符、函数和语法结构的交互方式,使得自定义对象能够像内置类型一样自然地使用。

命名约定

魔术方法的命名有特定的格式要求,它们都以双下划线__开头和结尾。这种命名约定使得这些方法在类中具有特殊的用途,不会与普通的方法名发生冲突。例如:

学习原因
  • 提升代码可读性和可维护性
  • 实现类的特殊行为和高级功能

常见魔术方法

初始化、删除、调用
  • __init__(self, ...):对象初始化方法(构造函数)
  • __new__(cls, ...):创建对象实例的方法,通常与__init__ 搭配使用
  • __del__(self):对象销毁方法(析构函数)
  • __call__(self, ...):对象被调用时,比如xxx()这样调用形式,就会调用__call__方法的内容
字符串表示
  • __str__(self):定义对象的字符串表示,适用于 str()print()
  • __repr__(self):定义对象的官方字符串表示,适用于 repr(),通常可通过 eval() 函数重建对象
  • __format__(self, format_spec):定义对象的格式化表示,适用于 format() 函数和格式化字符串
  • __bytes__(self):定义对象的字节串表示,适用于 bytes()
数值运算
  • __neg__(self):一元负号(取负),即 -self
  • __pos__(self):一元正号,即 +self
  • __abs__(self):绝对值,即 abs(self)
  • __invert__(self):按位取反,即 ~self
  • __add__(self, other):加法,即 self + other
  • __sub__(self, other):减法,即 self - other
  • __mul__(self, other):乘法,即 self * other
  • __matmul__(self, other):矩阵乘法,即 self @ other
  • __truediv__(self, other):真除法,即 self / other
  • __floordiv__(self, other):取整除法,即 self // other
  • __mod__(self, other):取模,即 self % other
  • __divmod__(self, other):同时计算取整除法和取模,返回 (self // other, self % other)
  • __pow__(self, other, modulo=None):幂运算,即 self ** other
  • __lshift__(self, other):左移位运算,即 self << other
  • __rshift__(self, other):右移位运算,即 self >> other
  • __and__(self, other):按位与,即 self & other
  • __xor__(self, other):按位异或,即 self ^ other
  • __or__(self, other):按位或,即 self | other
  • __radd__(self, other):反向加法,即 other + self
  • __rsub__(self, other):反向减法,即 other - self
  • __rmul__(self, other):反向乘法,即 other * self
  • __rmatmul__(self, other):反向矩阵乘法,即 other @ self
  • __rtruediv__(self, other):反向真除法,即 other / self
  • __rfloordiv__(self, other):反向取整除法,即 other // self
  • __rmod__(self, other):反向取模,即 other % self
  • __rdivmod__(self, other):反向同时计算取整除法和取模,返回 (other // self, other % self)
  • __rpow__(self, other, modulo=None):反向幂运算,即 other ** self
  • __rlshift__(self, other):反向左移位运算,即 other << self
  • __rrshift__(self, other):反向右移位运算,即 other >> self
  • __rand__(self, other):反向按位与,即 other & self
  • __rxor__(self, other):反向按位异或,即 other ^ self
  • __ror__(self, other):反向按位或,即 other | self
  • __iadd__(self, other):增量加法赋值,即 self += other
  • __isub__(self, other):增量减法赋值,即 self -= other
  • __imul__(self, other):增量乘法赋值,即 self *= other
  • __imatmul__(self, other):增量矩阵乘法赋值,即 self @= other
  • __itruediv__(self, other):增量真除法赋值,即 self /= other
  • __ifloordiv__(self, other):增量取整除法赋值,即 self //= other
  • __imod__(self, other):增量取模赋值,即 self %= other
  • __ipow__(self, other, modulo=None):增量幂运算赋值,即 self **= other
  • __ilshift__(self, other):增量左移位赋值,即 self <<= other
  • __irshift__(self, other):增量右移位赋值,即 self >>= other
  • __iand__(self, other):增量按位与赋值,即 self &= other
  • __ixor__(self, other):增量按位异或赋值,即 self ^= other
  • __ior__(self, other):增量按位或赋值,即 self |= other
类型转换
  • __int__(self):定义对象到整数的转换,适用于 int()
  • __float__(self):定义对象到浮点数的转换,适用于 float()
  • __complex__(self):定义对象到复数的转换,适用于 complex()
  • __bool__(self):定义对象的布尔值转换,适用于 bool()
  • __index__(self):定义对象作为索引的转换,适用于切片操作和 bin()hex()oct() 函数
集合操作
  • __len__(self):定义对象的长度,适用于 len()
  • __getitem__(self, key):定义按键访问,适用于 obj[key]
  • __setitem__(self, key, value):定义按键赋值,适用于 obj[key] = value
  • __delitem__(self, key):定义按键删除,适用于 del obj[key]
  • __contains__(self, item):定义成员测试,适用于 item in obj
迭代协议
  • __iter__(self):定义返回迭代器,适用于 iter()
  • __next__(self):定义返回下一个元素,适用于 next()
上下文管理
  • __enter__(self):定义进入上下文管理时的行为,适用于 with 语句
  • __exit__(self, exc_type, exc_value, traceback):定义退出上下文管理时的行为,适用于 with 语句
属性访问
  • __getattr__(self, name):定义访问不存在的属性时的行为
  • __getattribute__(self, name):定义访问任何属性时的行为
  • __setattr__(self, name, value):定义设置属性时的行为
  • __delattr__(self, name):定义删除属性时的行为
描述符协议
  • __get__(self, instance, owner):定义描述符的获取行为
  • __set__(self, instance, value):定义描述符的设置行为
  • __delete__(self, instance):定义描述符的删除行为
比较操作
  • __lt__(self, other):小于,适用于 <
  • __le__(self, other):小于等于,适用于 <=
  • __eq__(self, other):等于,适用于 ==
  • __ne__(self, other):不等于,适用于 !=
  • __gt__(self, other):大于,适用于 >
  • __ge__(self, other):大于等于,适用于 >=

常见应用示例

字符串表示

__str__ 方法用于定义对象的字符串表示,通常用于用户友好的输出,而 __repr__ 方法用于定义对象的官方字符串表示,通常可以用来重建对象。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

point = Point(3, 4)
print(str(point))  # 输出:(3, 4)
print(repr(point))  # 输出:Point(3, 4)
排序和比较

通过实现一系列比较运算的魔术方法,可以定义自定义类的排序和比较行为。

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def __lt__(self, other):
        return self.score < other.score

    def __eq__(self, other):
        return self.score == other.score

    def __repr__(self):
        return f"Student({self.name}, {self.score})"

students = [
    Student("Alice", 85),
    Student("Bob", 75),
    Student("Charlie", 90)
]
# 根据分数排序
sorted_students = sorted(students)
print(sorted_students)

# 输出:[Student(Bob, 75), Student(Alice, 85), Student(Charlie, 90)]

在这个例子中,通过实现 __lt__ 方法,定义了对象的小于比较。因此可以使用 sorted() 函数对学生列表进行排序。

实现数值类

通过重载算术运算符的魔术方法,可以实现自定义数值类,如复数或矩阵。

class ComplexNumber:
    def __init__(self, real, imag):
        self.real = real
        self.imag = imag

    def __add__(self, other):
        return ComplexNumber(
            self.real + other.real,
            self.imag + other.imag
        )

    def __mul__(self, other):
        return ComplexNumber(
            self.real * other.real - self.imag * other.imag,
            self.real * other.imag + self.imag * other.real
        )

    def __repr__(self):
        return f"{self.real} + {self.imag}i"

# 创建两个复数
c1 = ComplexNumber(2, 3)
c2 = ComplexNumber(4, 5)

# 复数加法
print(c1 + c2)  # 输出:6 + 8i

# 复数乘法
print(c1 * c2)  # 输出:-7 + 22i

在这个例子中,通过重载 __add____mul__ 方法,实现了复数的加法和乘法

集合与序列协议

通过实现集合与序列协议相关的魔术方法,可以定义自定义容器类,使其支持索引和迭代。

比如我们要实现一个既能排序又能去重的SortedSet类:

class SortedSet:
    def __init__(self, items=None):
        self._items = sorted(set(items)) if items else []

    def __repr__(self):
        return f"SortedSet({self._items})"

    def __len__(self):
        return len(self._items)

    def __contains__(self, item):
        return item in self._items

    def __iter__(self):
        return iter(self._items)

    def __getitem__(self, index):
        return self._items[index]

    def add(self, item):
        if item not in self._items:
            self._items.append(item)
            self._items.sort()

    def remove(self, item):
        if item in self._items:
            self._items.remove(item)

# 测试 SortedSet 类
s = SortedSet([3, 1, 2, 3, 4])
print(s)           # 输出:SortedSet([1, 2, 3, 4])
print(len(s))      # 输出:4
print(2 in s)      # 输出:True
print(s[0])        # 输出:1
s.add(5)
print(s)           # 输出:SortedSet([1, 2, 3, 4, 5])
s.remove(2)
print(s)           # 输出:SortedSet([1, 3, 4, 5])
属性访问控制

通过实现属性访问相关的魔术方法,可以实现动态属性和代理模式。

class DynamicAttribute:
    def __getattr__(self, name):
        if name == "age":
            return 25
        elif name == "name":
            return "Alice"
        else:
            raise AttributeError(
                f"'DynamicAttribute' object has no attribute '{name}'"
            )

obj = DynamicAttribute()
print(obj.age)  # 输出:25
print(obj.name)  # 输出:Alice

在这个例子中,通过实现 __getattr__ 方法,动态地为对象添加属性。如果属性不存在,则会调用这个方法来获取属性值,这在Python一些的ORM框架中很常见。

构造查询语句

python中的运算符|&其实可以对标SQL中的orand,所以我们可以通过魔术方法来实现一些类似SQL语句拼接

# 注:代码块暂不考虑太多异常情况,比如not操作
class Q:
    def __init__(self, *expressions, operator="and", **kwargs):
        self.operator = operator
        self._expressions = expressions
        self._kwargs = kwargs

    def __or__(self, other):
        return Q(self, other, operator="or")

    def __str__(self):
        return self.translate()

    def translate(self):
        kv_sql = " and ".join([f"{k}={v}" for k, v in self._kwargs.items()])
        expr_sql = f" {self.operator} ".join([expr.translate() for expr in self._expressions])
        return expr_sql + (f" {self.operator} " + kv_sql if expr_sql and kv_sql else kv_sql)


# 示例用法
q1 = Q(name='Alice', height=168)
q2 = Q(age=25, gender=0)
q3 = Q(q1, age=25)

# 实现 OR 运算符
print(q1 | q2)
# 输出:name=Alice and height=168 or age=25 and gender=0
print(q3)
# 输出:name=Alice and height=168 and age=25

上面例子中kwargs参数代表的是参与and操作的拼接键值对参数,而|则表示两个对象要进行or操作的拼接,这样就可以实现类似ORM框架里的还原SQL字符串 拼接操作了

上下文管理

上下文管理是 Python 中用于管理资源的一种机制,它可以确保在进入和离开代码块时资源得到正确的管理和释放。在使用 with 语句时,Python 会调用对象的 __enter__ 方法来获取上下文管理器,并在离开代码块时调用 __exit__ 方法来释放资源。

class MyResource:
    def __enter__(self):
        print("Entering the resource")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the resource")

    def operation(self):
        print("Performing operation")

# 使用上下文管理器
with MyResource() as resource:
    resource.operation()

# Outputs
# Entering the resource
# Performing operation
# Exiting the resource

在这个示例中,MyResource 类实现了上下文管理器。当进入 with 代码块时,Python 会调用 __enter__ 方法,然后执行其中的代码;当离开 with 代码块时,Python 会调用 __exit__ 方法,以确保资源被正确释放。

可调用对象

__call__ 方法使得一个对象可以像函数一样被调用,这对于实现可调用对象非常有用。当调用对象时,Python 解释器会自动调用对象的 __call__ 方法。

class MyCallable:
    def __call__(self, x):
        return x * 2

# 创建可调用对象
callable_obj = MyCallable()

# 调用可调用对象
result = callable_obj(5)
print(result)  # 输出:10

在这个示例中,MyCallable 类实现了 __call__ 方法,使得它的实例可以像函数一样被调用。当调用 callable_obj(5) 时,Python 实际上调用了 callable_obj.__call__(5)

__new____init__

__new__ 方法和 __init__ 方法都是 Python 中用于创建类实例的特殊方法,但它们的作用略有不同。

__new__ 方法在实例创建之前调用,用于创建实例对象,并返回一个新创建的实例。
__init__ 方法在实例创建之后调用,用于初始化实例对象的属性。

class MyClass:
    def __new__(cls, *args, **kwargs):
        print("Creating instance")
        instance = super().__new__(cls)
        return instance

    def __init__(self, x):
        print("Initializing instance")
        self.x = x

# 创建实例
obj = MyClass(5)

# Outputs
# Creating instance
# Initializing instance

在这个示例中,__new__ 方法在实例创建之前被调用,用于创建实例对象,而 __init__ 方法在实例创建之后被调用,用于初始化实例对象的属性。

总结

Python 魔术方法是一种特殊的命名约定,用双下划线包围,用于实现对象的特殊行为和操作。它们包括但不限于初始化、字符串表示、比较、算术运算、上下文管理等方面。通过合理使用魔术方法,可以提高代码的可读性、可维护性,并实现更灵活的编程逻辑,是面向对象编程里一种非常重要的实现方法。