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
煅魂-JeffreyChu的修炼屋 – 做一个胡思乱想的程序员

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

sqlparse,除了解析还能做什么

之前在项目开发中发现每个人编写的SQL格式都有点不一样,比如缩进/关键字大小写不一样,当时就在想有没有类似Python的Black库那样可以格式化Python代码来保持一样代码风格?

那就是今天要介绍的sqlparse库了。

简介

sqlparse是一个用来解析SQL查询语句的轻量级Python库。它并不执行SQL查询操作,而是专注于解析和格式化SQL语句。它可以帮助开发者处理SQL代码的缩进、格式化以及拆分复杂查询,甚至支持对查询结构的分析。

  • 格式化:对凌乱的SQL语句进行格式化,输出可读性强的代码。
  • 解析:将SQL语句解析为结构化的token流,便于进一步的操作。
  • SQL分析:支持对SQL语句中的结构进行深入分析,识别出关键字表名列名函数等组件。

安装

在开始使用sqlparse之前,我们需要先安装该库。你可以通过Python的包管理工具pip进行安装:

pip install sqlparse

安装完成后,便可以在项目中导入并使用sqlparse了。

基础用法

sqlparse的核心功能主要包括两部分:格式化和解析。

格式化

SQL语句的格式化是sqlparse最基本的功能之一。通过简单的函数调用,开发者可以将一行SQL语句自动格式化为多行、缩进合理的代码,从而提高可读性。

假设有一个复杂的SQL语句,它在一行中很难阅读:

import sqlparse

sql =  """
select a.id, b.key, c.name, d.addr, f.mid from a left join b on a.id = b.id left join c on a.name = c.name left join d on d.key = b.key left join (select e.addr, f.mid from e join f on e.mid = f.mid) t on t.addr = d.addr WHERE b.key IS NOT NULL and c.name != '' order by a.id desc limit 100
"""
formatted_sql = sqlparse.format(sql, reindent=True, keyword_case='upper')
print(formatted_sql)

运行这段代码后,你将得到一个格式化后的SQL语句:

SELECT a.id,
       b.key,
       c.name,
       d.addr,
       f.mid
FROM a
LEFT JOIN b ON a.id = b.id
LEFT JOIN c ON a.name = c.name
LEFT JOIN d ON d.key = b.key
LEFT JOIN
  (SELECT e.addr,
          f.mid
   FROM e
   JOIN f ON e.mid = f.mid) t ON t.addr = d.addr
WHERE b.key IS NOT NULL
  AND c.name != ''
ORDER BY a.id DESC
LIMIT 100

sqlparse.format()函数提供了许多可选参数,使开发者能够自定义格式化的行为:

  • keyword_case:用于控制关键字的大小写。可选值包括:
    • 'upper':将关键字转换为大写。
    • 'lower':将关键字转换为小写。
  • reindent:启用后将自动重新缩进SQL代码,默认值为False
  • indent_width:指定缩进的宽度,默认为2
  • strip_comments:启用后将删除SQL语句中的所有注释,默认值为False

所以,我们可以启用多个选项来生成更加个性化的格式, 以美化我们平常写出来的复杂的SQL语句。

解析

sqlparse不仅可以格式化SQL,还可以对SQL进行解析。它可以将SQL语句拆分成多个token,便于开发者对SQL的结构进行深入分析。

基本解析

我们可以使用sqlparse.parse()函数来解析SQL语句,并生成一个Statement对象列表,其中每个Statement对象表示一条SQL查询语句。

import sqlparse

sql = """
select id, name, age from users limit 10;
select id, name from coms limit 10;
"""
parsed = sqlparse.parse(sql)
for index, stmt in enumerate(parsed):
    print(index, ":", stmt.value.strip())

输出:

0 : select id, name, age from users limit 10;
1 : select id, name from coms limit 10;

这将输出被解析的SQL语句的结构。在这里,parsed是一个包含Statement对象的列表,开发者可以对每个Statement对象进行进一步操作。

Token 的概念

sqlparse使用Token的概念来表示SQL语句中的每一个组成部分,包括关键字、表名、操作符等。通过sqlparse,我们可以轻松提取这些信息。下面是一个简单的示例:

import sqlparse

sql = """select id, name, age from users limit 10;"""

parsed = sqlparse.parse(sql)

for stmt in parsed:
    for token in stmt.tokens:
        print(token.ttype, token.value)

输出:

Token.Keyword.DML select
Token.Text.Whitespace  
None id, name, age
Token.Text.Whitespace  
Token.Keyword from
Token.Text.Whitespace  
None users
Token.Text.Whitespace  
Token.Keyword limit
Token.Text.Whitespace  
Token.Literal.Number.Integer 10
Token.Punctuation ;

这段代码将逐个输出每个token的类型和值。ttypetoken的类型,如关键字运算符等,而valuetoken的实际值。

Token类型分类

sqlparse库中的Token类型被分类为多个类别,如:

  • KeywordSQL中的关键字,如SELECTFROM等。
  • Identifier:表名、列名等标识符。
  • Literal:字面值常量,如数字和字符串。
  • OperatorSQL中的操作符,如=><等。开发者可以根据需求对这些不同类型的token进行分类或过滤。

SQL分析

借助sqlparse的解析能力,我们可以进一步对SQL结构进行分析。这对复杂的SQL查询尤为有用,尤其是当开发者需要从查询中提取表名列名或其他组件时。

提取表名

通过sqlparse,我们可以轻松提取出SQL查询语句中使用的表名。以下是一个简单的示例:

import sqlparse

from sqlparse.sql import Identifier
from sqlparse.tokens import Keyword

def extract_tables(sql):
    parsed = sqlparse.parse(sql)
    stmt = parsed[0]
    tables = []
    for token in stmt.tokens:
        if isinstance(token, Identifier):
            tables.append(token.get_real_name())
        elif token.ttype is Keyword and token.value.upper() == 'FROM':
            pass
    return tables

sql = "SELECT id, name FROM users u JOIN orders o ON u.id = o.user_id"
tables = extract_tables(sql)
print(tables)

该函数会解析SQL并返回查询中涉及到的所有表名。在这个例子中,输出为:

['users', 'orders']

提取列名

类似地,我们也可以提取SQL查询中的列名。这个操作非常适合用于需要对SQL查询进行静态分析的场景。

import sqlparse

from sqlparse.sql import IdentifierList

def extract_columns(sql):
    parsed = sqlparse.parse(sql)
    stmt = parsed[0]
    columns = []
    for token in stmt.tokens:
        if isinstance(token, IdentifierList):
            for identifier in token.get_identifiers():
                columns.append(identifier.get_real_name())
    return columns

sql = "SELECT id, name, age FROM users"
columns = extract_columns(sql)
print(columns)

输出结果将为:

['id', 'name', 'age']

高级用法

在前面的基础功能介绍之外,sqlparse还提供了一些高级特性,帮助开发者处理更加复杂的SQL查询。

SQL语句拆分

sqlparse支持对多条SQL语句进行拆分,这对需要批量处理或分析多个SQL查询时非常有用。以下示例展示了如何使用sqlparse.split()函数来拆分多条SQL语句:

import sqlparse

sql = "SELECT * FROM users; INSERT INTO users(id, name) VALUES (1, 'Alice');"
statements = sqlparse.split(sql)
for statement in statements:
    print(statement)

输出:

SELECT * FROM users;
INSERT INTO users(id, name) VALUES (1, 'Alice');

sqlparse.split()函数会根据SQL中的分号(;)将SQL语句拆分成多个独立的语句。

定制化解析

除了内置的Token类型和解析逻辑,sqlparse还允许你定制解析过程。通过对Statement对象的深入分析,开发者可以编写自定义规则来处理复杂的SQL查询。

过滤器

sqlparse中的过滤器机制可以让开发者在解析过程中动态操作Token流。你可以通过编写过滤器来操作SQL结构中的各个部分,例如关键字标识符表达式

例如,我们可以编写一个简单的过滤器,将所有SQL关键字转换为小写(这个其实可以用sqlparse.format()自身也可以实现):

import sqlparse

class LowerKeywordFilter:
    def process(self, stmt):
        def process_token(token):
            if token.is_group:
                for sub_token in token.tokens:
                    process_token(sub_token)
            elif token.is_keyword:
                token.value = token.value.lower()
            return token

        for token in stmt.tokens:
            process_token(token)

        return stmt

sql = "SELECT ID, NAME FROM USERS WHERE AGE > 30;"
parsed = sqlparse.parse(sql)[0]
lower_keyword_filter = LowerKeywordFilter()
processed_stmt = lower_keyword_filter.process(parsed)
print(processed_stmt)

输出结果:

select ID, NAME from USERS where AGE > 30;

通过过滤器,开发者可以对SQL语句中的任何元素进行操作。

SQL格式定制化

除了基础的SQL格式化功能外,sqlparse还允许开发者根据需求进一步定制输出格式。

比如。忽略部分元素。sqlparse允许你在格式化时忽略某些元素。例如,如果SQL语句中包含注释,但你希望忽略这些注释,可以使用strip_comments=True选项:

import sqlparse

sql = "SELECT ID, NAME -- this is a comment\nFROM USERS"
formatted_sql = sqlparse.format(sql, reindent=True, strip_comments=True)
print(formatted_sql)

输出结果:

SELECT ID,
       NAME
FROM USERS

注释被自动移除,这对在生产环境中处理分析大量SQL查询时可能非常有用。

SQL代码审查

sqlparse不仅仅是可以用于分析SQL代码,还可以用于审查SQL,提升SQL安全性和规范性。

比如在项目过程中硬性规定创建一个表设计不能超过5个普通索引,那我们可以拉出数据库的建表语句或者保留在项目中DDL(Data Definition Language,数据定义语言) 文件, 通过sqlparse检查脚本检测哪些已存在的或准备提交创建的表不合规(当然这只是其中一种思路,方法不只一种)

import sqlparse

def check_indexes_count(ddl_statement):
    index_count = 0
    parsed = sqlparse.parse(ddl_statement)
    for statement in parsed:
        tokens = statement.flatten()
        for token in tokens:
            if token.ttype == sqlparse.tokens.Keyword and token.value.upper() == 'INDEX':
                index_count += 1
    return index_count > 5

ddl = """CREATE TABLE my_table (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    address INT,
    INDEX idx_name (name),
    INDEX idx_age_name (age, name),
    INDEX idx_another (id),
    INDEX idx_more (age),  
    INDEX idx_extra (name),  
    INDEX idx_address (address)
);"""
if check_indexes_count(ddl):
    print(f"该DDL语句中有超过5个索引。")
else:
    print(f"该DDL语句中没有超过5个索引。")

输出:

该DDL语句中有超过5个索引

甚至我们在单元测试的时候,审查级别还可以下沉到DML(Data Manipulation Language, 数据操作语言) 即将操作的层面来进行SQL分析审查,比如检查改查询语句where条件里是否用了非索引字段进行查询,是否有SQL注入风险等

应用场景

由于sqlparse具备灵活的SQL解析与格式化功能,它在多个开发场景中扮演着重要角色。以下是sqlparse的一些典型应用场景:

  • 在开发过程中,SQL查询语句常常会变得复杂且难以阅读。sqlparse可以自动对SQL进行格式化,确保查询语句结构清晰、缩进合理。这在代码审查中尤为重要,有助于提高SQL代码的可读性维护性
  • 使用sqlparse的解析功能,开发者可以自动化地分析SQL查询,提取表名列名关键字索引等信息。这种静态分析能力可以帮助检测潜在的SQL性能问题,并为查询优化提供数据支持。
  • 在数据库系统中,SQL查询日志记录了用户的所有查询操作。sqlparse可以用于解析和分析这些日志,帮助开发者追踪查询行为、检测潜在的安全问题或生成审计报告。
  • 在数据迁移和管理过程中,sqlparse可以解析复杂的SQL脚本,自动调整查询结构或生成新的SQL语句。这对于大规模数据库迁移或自动化SQL脚本生成非常有用。

总结

sqlparse不仅是一个简单的SQL格式化工具,它还提供了强大的SQL解析功能。

通过使用sqlparse,开发者可以简化复杂的SQL查询操作,提升SQL代码的可读性,并为静态分析审查与查询优化提供坚实的基础。

如果你觉得本文对您有一点点帮助的话,希望能得到您的一点点支持,也欢迎在评论区说出您自己的见解。

听说AI会写古诗了,真的吗?

之前听闻GPT之类的AI大模型写歌写诗非常6了,今天刚好中秋佳节,来看看AI们是怎么描绘中秋月夜的。
中秋节

大致步骤

这里我打算让各AI先熟悉学习古代一些出名的诗人的古诗风格,然后在根据我的主题要求进行创作

分析与学习

目标诗人

  • 李白
  • 杜甫
  • 王维

古诗来源

数据分析主要用了李白、杜甫、王维过往的各10首古诗,主要是七言律诗或五言律诗,主题不限

  • 《登金陵凤凰台》
  • 《别中都明府兄》
  • 《题东溪公幽居》
  • 《送贺监归四明应制》
  • 《对酒忆贺监二首・其一》
  • 《陪族叔刑部侍郎晔及中书贾舍人至游洞庭五首・其二》
  • 《鹦鹉洲》
  • 《巴陵赠贾舍人》
  • 《题雍丘崔明府丹灶》
  • 《流夜郎赠辛判官》
  • 《登高》
  • 《蜀相》
  • 《闻官军收河南河北》
  • 《咏怀古迹五首・其三》
  • 《客至》
  • 《野望》
  • 《宿府》
  • 《阁夜》
  • 《九日蓝田崔氏庄》
  • 《又呈吴郎》
  • 《积雨辋川庄作》
  • 《奉和圣制从蓬莱向兴庆阁道中留春雨中春望之作应制》
  • 《和贾舍人早朝大明宫之作》
  • 《送杨少府贬郴州》
  • 《酌酒与裴迪》
  • 《过香积寺》
  • 《辋川别业》
  • 《早秋山中作》
  • 《送梓州李使君》
  • 《冬晚对雪忆胡居士家》

模型选择与训练

  • 豆包
  • 文心一言
  • chatGPT

生成与调整

提示词设定主题和要求:

  • 仿写的古诗要求诗七言律诗
  • 要求内容描述中秋佳节非常热闹,人们生活物质水平提高,想吃啥就吃啥,大鱼大肉
  • 第三主题主要表现为虽然生活水平提高了,但依旧怀念以前小时候能吃上一个烤翅就很开心的日子,抒发了“欲买桂花同载酒,终不似,少年游”的情感
  • 根据提供仿写的30首古诗,先自行分析其结构特点以及主题内容,然后根据上面的要求进行仿写一首关于中秋古诗

结果

豆包

豆包

文心一言

文心一言

chatGPT

chatGPT

总结

再不做进一步的优化情况下,我比较喜欢文心一言的那首,但三首都没有展示出古诗原有的那种蕴味,AI们还需要继续努力!

最后,祝大家中秋节快乐!