像写 Rust 一样写 Python!

告别命令行!在VSCode里像写Python一样玩转Rust:从Hello World到断点调试(CodeLLDB配置详解) 本文详细介绍了如何在VSCode中配置Rust开发环境,实现从Hello World到断点调试的无缝体验。通过安装rust-analyzer和CodeLLDB插件,开发者可以像编写Python一样轻松开发Rust程序,享受代码补全、一键运行和断点调试等现代IDE功能。文章还提供了CodeLLDB的详细配置指南和常见问题解决方案,帮助开发者快速上手Rust开发。 阅读详情

几年前,我开始使用Rust编程,它逐渐改变了我使用其他编程语言(尤其是Python)设计程序的方式。在我开始使用Rust之前,我通常以一种非常动态和类型松散的方式编写Python代码,没有类型提示,到处传递和返回字典,偶尔回退到“字符串类型”接口。然而,在经历了Rust类型系统的严格性,并注意到它“通过构造”防止的所有问题之后,每当我回到Python并且没有得到相同的保证时,我突然变得非常焦虑。

需要明确的是,这里的“保证”并不是指内存安全(Python本身是合理的内存安全),而是“稳健性”——设计很难或完全不可能被滥用的API的概念,从而防止未定义的行为和各种错误。在Rust中,错误使用的接口通常会导致编译错误。在Python中,您仍然可以执行此类不正确的程序,但如果您使用类型检查器(如pyright)或带有类型分析器的IDE(如PyCharm),您仍然可以获得类似级别的有关可能问题的快速反馈。

最终,我开始在我的Python程序中采用Rust的一些概念。它基本上可以归结为两件事——尽可能多地使用类型提示,并坚持让非法状态无法表示的原则。我尝试对将维护一段时间的程序和 oneshot实用程序脚本都这样做。主要是因为根据我的经验,后者经常变成前者:)根据我的经验,这种方法导致程序更容易理解和更改。

在本文中,我将展示几个应用于Python程序的此类模式示例。这不是火箭科学,但我仍然觉得记录它们可能会有用。

注意:这篇文章包含了很多关于编写Python代码的观点。我不想在每句话中都加上“恕我直言”,所以将这篇文章中的所有内容仅作为我对此事的看法,而不是试图宣传一些普遍的真理:)另外,我并不是说所提出的想法是所有这些都是在Rust中发明的,当然,它们也被用于其他语言。

Type hint

首要的是尽可能使用类型提示,特别是在函数签名和类属性中。当我读到一个像这样的函数签名时:

def find_item(records, check):

我不知道签名本身发生了什么。是records列表、字典还是数据库连接?是check布尔值还是函数?这个函数返回什么?如果失败会发生什么,它会引发异常还是返回None?为了找到这些问题的答案,我要么必须去阅读函数体(并且经常递归地阅读它调用的其他函数的函数体——这很烦人),要么阅读它的文档(如果有的话)。虽然文档可能包含有关函数功能的有用信息,但没有必要将它也用于记录前面问题的答案。很多问题都可以通过内置机制——类型提示——来回答。

def find_item(
  records: List[Item],
  check: Callable[[Item], bool]
) -> Optional[Item]:

我写签名花了更多时间吗?是的。那是问题吗?不,除非我的编码受到每分钟写入的字符数的瓶颈,而这并没有真正发生。明确地写出类型迫使我思考函数提供的实际接口是什么,以及如何使其尽可能严格,以使其调用者难以以错误的方式使用它。通过上面的签名,我可以很好地了解如何使用该函数、将什么作为参数传递给它以及我期望从中返回什么。此外,与代码更改时很容易过时的文档注释不同,当我更改类型并且不更新函数的调用者时,类型检查器会对我大喊大叫。如果我对什么是Item感兴趣,我可以直接使用Go to definition并立即查看该类型的外观。

在这方面,我不是一个绝对主义者,如果需要五个嵌套类型提示来描述单个参数,我通常会放弃并给它一个更简单但不精确的类型。根据我的经验,这种情况不会经常发生。如果它确实发生了,它实际上可能表明代码有问题——如果你的函数参数可以是一个数字、一个字符串元组或一个将字符串映射到整数的字典,这可能表明你可能想要重构和简化它。

数据类(dataclass)而不是元组(tuple)或字典(dictionary)

使用类型提示是一回事,但这仅仅描述了函数的接口是什么。第二步实际上是使这些接口尽可能精确和“锁定”。一个典型的例子是从一个函数返回多个值(或一个复杂的值)。懒惰而快速的方法是返回一个元组:

def find_person(...) -> Tuple[str, str, int]:

太好了,我们知道我们要返回三个值。这些是什么?第一个字符串是人的名字吗?第二串姓氏?电话号码是多少?是年龄吗?在某些列表中的位置?社会安全号码?这种输入是不透明的,除非你查看函数体,否则你不知道这里发生了什么。

下一步“改进”这可能是返回一个字典:

def find_person(...) -> Dict[str, Any]:
    ...
    return {
        "name": ...,
        "city": ...,
        "age": ...
    }

现在我们实际上知道各个返回的属性是什么,但我们必须再次检查函数体才能找出答案。从某种意义上说,类型变得更糟,因为现在我们甚至不知道各个属性的数量和类型。此外,当这个函数发生变化并且返回的字典中的键被重命名或删除时,没有简单的方法可以用类型检查器找出来,因此它的调用者通常必须用非常手动和烦人的运行-崩溃-修改代码来改变循环。

正确的解决方案是返回一个强类型对象,其命名参数具有附加类型。在Python中,这意味着我们必须创建一个类。我怀疑在这些情况下经常使用元组和字典,因为它比定义类(并为其命名)、创建带参数的构造函数、将参数存储到字段等容易得多。自Python 3.7 (并且更快地使用package polyfill),有一个更快的解决方案-dataclasses.

@dataclasses.dataclass
class City:
    name: str
    zip_code: int


@dataclasses.dataclass
class Person:
    name: str
    city: City
    age: int


def find_person(...) -> Person:

你仍然需要为创建的类考虑一个名称,但除此之外,它已经尽可能简洁了,并且你可以获得所有属性的类型注释。

有了这个数据类,我就有了函数返回内容的明确描述。当我调用此函数并处理返回值时,IDE自动完成功能将向我显示其属性的名称和类型。这听起来可能微不足道,但对我来说这是一个巨大的生产力优势。此外,当代码被重构并且属性发生变化时,我的IDE和类型检查器将对我大喊大叫并向我显示所有必须更改的位置,而我根本不必执行程序。对于一些简单的重构(例如属性重命名),IDE甚至可以为我进行这些更改。此外,通过明确命名的类型,我可以构建术语词汇表( Person,City),然后可以与其他函数和类共享。

代数数据类型

在大多数主流语言中,我可能最缺乏的Rust是代数数据类型(ADT)2。它是一个非常强大的工具,可以明确描述我的代码正在处理的数据的形状。例如,当我在Rust中处理数据包时,我可以显式枚举所有可以接收的各种数据包,并为它们中的每一个分配不同的数据(字段):

enum Packet {
  Header {
    protocol: Protocol,
    size: usize
  },
  Payload {
    data: Vec<u8>
  },
  Trailer {
    data: Vec<u8>,
    checksum: usize
  }
}

通过模式匹配,我可以对各个变体做出反应,编译器会检查我没有遗漏任何情况:

fn handle_packet(packet: Packet) {
  match packet {
    Packet::Header { protocol, size } => ...,
    Packet::Payload { data } |
    Packet::Trailer { data, ...} => println!("{data:?}")
  }
}

这对于确保无效状态不可表示并因此避免许多运行时错误是非常宝贵的。ADT在静态类型语言中特别有用,如果你想以统一的方式使用一组类型,你需要一个共享的“名称”来引用它们。如果没有ADT,这通常是使用OOP接口和/或继承来完成的。当使用的类型集是开放式的时,接口和虚方法有它们的位置,但是当类型集是封闭的,并且你想确保你处理所有可能的变体时,ADT和模式匹配更合适。

在动态类型语言(如Python)中,实际上不需要为一组类型共享名称,主要是因为您甚至不必一开始就为程序中使用的类型命名。但是,通过创建联合类型,使用类似于ADT的东西仍然有用:

@dataclass
class Header:
  protocol: Protocol
  size: int

@dataclass
class Payload:
  data: str

@dataclass
class Trailer:
  data: str
  checksum: int

Packet = typing.Union[Header, Payload, Trailer]
# or `Packet = Header | Payload | Trailer` since Python 3.10

Packet这里定义了一个新类型,它可以是报头、有效载荷或尾部数据包。当我想确保只有这三个类有效时,我现在可以在程序的其余部分中使用此类型(名称)。请注意,类没有附加明确的“标签”,因此当我们要区分它们时,我们必须使用eginstanceof或模式匹配:

def handle_is_instance(packet: Packet):
    if isinstance(packet, Header):
        print("header {packet.protocol} {packet.size}")
    elif isinstance(packet, Payload):
        print("payload {packet.data}")
    elif isinstance(packet, Trailer):
        print("trailer {packet.checksum} {packet.data}")
    else:
        assert False

def handle_pattern_matching(packet: Packet):
    match packet:
        case Header(protocol, size): print(f"header {protocol} {size}")
        case Payload(data): print("payload {data}")
        case Trailer(data, checksum): print(f"trailer {checksum} {data}")
        case _: assert False

可悲的是,在这里我们必须(或者更确切地说,应该)包括烦人的assert False分支,以便函数在接收到意外数据时崩溃。在Rust中,这将是一个编译时错误。

注意:Reddit上的几个人已经提醒我,assert False实际上在优化构建( ) 中完全优化掉了python -O ...。因此,直接引发异常会更安全。还有typing.assert_never来自Python 3.11 的,它明确地告诉类型检查器落到这个分支应该是一个“编译时”错误。

联合类型的一个很好的属性是它是在作为联合一部分的类之外定义的。因此该类不知道它被包含在联合中,这减少了代码中的耦合。您甚至可以使用相同的类型创建多个不同的联合:

Packet = Header | Payload | Trailer
PacketWithData = Payload | Trailer

联合类型对于自动(反)序列化也非常有用。最近我发现了一个很棒的序列化库,叫做pyserde,它基于古老的Rustserde序列化框架。在许多其他很酷的功能中,它能够利用类型注释来序列化和反序列化联合类型,而无需任何额外代码:

import serde

...
Packet = Header | Payload | Trailer

@dataclass
class Data:
    packet: Packet

serialized = serde.to_dict(Data(packet=Trailer(data="foo", checksum=42)))
# {'packet': {'Trailer': {'data': 'foo', 'checksum': 42}}}

deserialized = serde.from_dict(Data, serialized)
# Data(packet=Trailer(data='foo', checksum=42))

你甚至可以选择联合标签的序列化方式,与serde.我一直在寻找类似的功能,因为它对(反)序列化联合类型非常有用。dataclasses_json但是,在我尝试过的大多数其他序列化库(例如或)中实现它非常烦人dacite。

例如,在使用机器学习模型时,我使用联合将各种类型的神经网络(例如分类或分段CNN模型)存储在单个配置文件格式中。我还发现对不同格式的数据(在我的例子中是配置文件)进行版本化很有用,如下所示:

Config = ConfigV1 | ConfigV2 | ConfigV3

通过反序列化Config,我能够读取所有以前版本的配置格式,从而保持向后兼容性。

使用newtype

在Rust中,定义不添加任何新行为的数据类型是很常见的,但只是用于指定其他一些非常通用的数据类型(例如整数)的域和预期用途。这种模式被称为“newtype”3,它也可以用在Python中。这是一个激励人心的例子:

class Database:
  def get_car_id(self, brand: str) -> int:
  def get_driver_id(self, name: str) -> int:
  def get_ride_info(self, car_id: int, driver_id: int) -> RideInfo:

db = Database()
car_id = db.get_car_id("Mazda")
driver_id = db.get_driver_id("Stig")
info = db.get_ride_info(driver_id, car_id)

发现错误?

……

……

的参数get_ride_info被交换。没有类型错误,因为汽车ID 和司机ID都是简单的整数,因此类型是正确的,即使在语义上函数调用是错误的。

我们可以通过使用“NewType”为不同类型的ID定义单独的类型来解决这个问题:

from typing import NewType

# Define a new type called "CarId", which is internally an `int`
CarId = NewType("CarId", int)
# Ditto for "DriverId"
DriverId = NewType("DriverId", int)

class Database:
  def get_car_id(self, brand: str) -> CarId:
  def get_driver_id(self, name: str) -> DriverId:
  def get_ride_info(self, car_id: CarId, driver_id: DriverId) -> RideInfo:


db = Database()
car_id = db.get_car_id("Mazda")
driver_id = db.get_driver_id("Stig")
# Type error here -> DriverId used instead of CarId and vice-versa
info = db.get_ride_info(<error>driver_id</error>, <error>car_id</error>)

这是一个非常简单的模式,可以帮助捕获难以发现的错误。它特别有用,例如,如果你正在处理许多不同类型的ID (CarId vs DriverId)或某些不应混合在一起的指标(Speed vs Lengthvs等)。Temperature

使用构造函数

我非常喜欢Rust的一件事是它本身没有构造函数。相反,人们倾向于使用普通函数来创建(理想情况下正确初始化)结构实例。在Python中,没有构造函数重载,因此如果您需要以多种方式构造一个对象,有人会导致一个__init__方法有很多参数,这些参数以不同的方式用于初始化,并且不能真正一起使用。

相反,我喜欢创建具有明确名称的“构造”函数,这使得如何构造对象以及从哪些数据构造对象变得显而易见:

class Rectangle:
    @staticmethod
    def from_x1x2y1y2(x1: float, ...) -> "Rectangle":
    
    @staticmethod
    def from_tl_and_size(top: float, left: float, width: float, height: float) -> "Rectangle":

这使得构造对象变得更加清晰,并且不允许类的用户在构造对象时传递无效数据(例如通过组合y1和width)。

使用类型编码不变量

使用类型系统本身来编码只能在运行时跟踪的不变量是一个非常通用和强大的概念。在Python(以及其他主流语言)中,我经常看到类是可变状态的毛茸茸的大球。这种混乱的根源之一是试图在运行时跟踪对象不变量的代码。它必须考虑理论上可能发生的许多情况,因为类型系统并没有使它们成为不可能(“如果客户端已被要求断开连接,现在有人试图向它发送消息,但套接字仍然是连接”等)。

Client

这是一个典型的例子:

class Client:
  """
  Rules:
  - Do not call `send_message` before calling `connect` and then `authenticate`.
  - Do not call `connect` or `authenticate` multiple times.
  - Do not call `close` without calling `connect`.
  - Do not call any method after calling `close`.
  """
  def __init__(self, address: str):

  def connect(self):
  def authenticate(self, password: str):
  def send_message(self, msg: str):
  def close(self):

……容易吧?你只需要仔细阅读文档,并确保你永远不会违反上述规则(以免调用未定义的行为或崩溃)。另一种方法是用各种断言填充类,这些断言会在运行时检查所有提到的规则,这会导致代码混乱、遗漏边缘情况以及出现错误时反馈速度较慢(编译时与运行时)。问题的核心是客户端可以存在于各种(互斥的)状态中,但不是单独对这些状态进行建模,而是将它们全部合并为一个类型。

让我们看看是否可以通过将各种状态拆分为单独的类型4来改进这一点。

首先,拥有一个Client不与任何东西相连的东西是否有意义?好像不是这样。这样一个未连接的客户端在您无论如何调用之前无法执行任何操作connect 。那么为什么要允许这种状态存在呢?我们可以创建一个调用的构造函数 connect,它将返回一个连接的客户端:

def connect(address: str) -> Optional[ConnectedClient]:
  pass

class ConnectedClient:
  def authenticate(...):
  def send_message(...):
  def close(...):

如果该函数成功,它将返回一个支持“已连接”不变量的客户端,并且你不能connect再次调用它来搞砸事情。如果连接失败,该函数可以引发异常或返回None或一些显式错误。

类似的方法可以用于状态authenticated。我们可以引入另一种类型,它保持客户端已连接并已通过身份验证的不变性:

class ConnectedClient:
  def authenticate(...) -> Optional["AuthenticatedClient"]:

class AuthenticatedClient:
  def send_message(...):
  def close(...):

只有当我们真正拥有an的实例后AuthenticatedClient,我们才能真正开始发送消息。

最后一个问题是方法close。在 Rust 中(由于 破坏性移动语义),我们能够表达这样一个事实,即当close调用方法时,您不能再使用客户端。这在 Python 中是不可能的,所以我们必须使用一些变通方法。一种解决方案可能是回退到运行时跟踪,在客户端中引入布尔属性,并断言close它send_message尚未关闭。另一种方法可能是close完全删除该方法并仅将客户端用作上下文管理器:

with connect(...) as client:
    client.send_message("foo")
# Here the client is closed

没有close可用的方法,你不能意外关闭客户端两次。

强类型边界框

对象检测是我有时从事的一项计算机视觉任务,其中程序必须检测图像中的一组边界框。边界框基本上是带有一些附加数据的美化矩形,当你实现对象检测时,它们无处不在。关于它们的一个恼人的事情是有时它们被规范化(矩形的坐标和大小在interval中[0.0, 1.0]),但有时它们被非规范化(坐标和大小受它们所附图像的尺寸限制)。当你通过许多处理数据预处理或后处理的函数发送边界框时,很容易把它搞砸,例如两次规范化边界框,这会导致调试起来非常烦人的错误。

这在我身上发生过几次,所以有一次我决定通过将这两种类型的bbox分成两种不同的类型来彻底解决这个问题:

@dataclass
class NormalizedBBox:
  left: float
  top: float
  width: float
  height: float


@dataclass
class DenormalizedBBox:
  left: float
  top: float
  width: float
  height: float

通过这种分离,规范化和非规范化的边界框不再容易混合在一起,这主要解决了问题。但是,我们可以进行一些改进以使代码更符合人体工程学:

  • 通过组合或继承减少重复:

@dataclass
class BBoxBase:
  left: float
  top: float
  width: float
  height: float

# Composition
class NormalizedBBox:
  bbox: BBoxBase

class DenormalizedBBox:
  bbox: BBoxBase

Bbox = Union[NormalizedBBox, DenormalizedBBox]

# Inheritance
class NormalizedBBox(BBoxBase):
class DenormalizedBBox(BBoxBase):
  • 添加运行时检查以确保规范化的边界框实际上是规范化的:

class NormalizedBBox(BboxBase):
  def __post_init__(self):
    assert 0.0 <= self.left <= 1.0
    ...
  • 添加一种在两种表示之间进行转换的方法。在某些地方,我们可能想知道显式表示,但在其他地方,我们想使用通用接口(“任何类型的 BBox”)。在那种情况下,我们应该能够将“任何 BBox”转换为以下两种表示之一:

class BBoxBase:
  def as_normalized(self, size: Size) -> "NormalizeBBox":
  def as_denormalized(self, size: Size) -> "DenormalizedBBox":

class NormalizedBBox(BBoxBase):
  def as_normalized(self, size: Size) -> "NormalizedBBox":
    return self
  def as_denormalized(self, size: Size) -> "DenormalizedBBox":
    return self.denormalize(size)

class DenormalizedBBox(BBoxBase):
  def as_normalized(self, size: Size) -> "NormalizedBBox":
    return self.normalize(size)
  def as_denormalized(self, size: Size) -> "DenormalizedBBox":
    return self

有了这个界面,我可以两全其美——为了正确性而分开的类型,以及为了人体工程学而使用统一的界面。

注意:如果你想向返回相应类实例的父类/基类添加一些共享方法,你可以typing.Self从Python 3.11 开始使用:

class BBoxBase:
  def move(self, x: float, y: float) -> typing.Self: ...

class NormalizedBBox(BBoxBase):
  ...

bbox = NormalizedBBox(...)
# The type of `bbox2` is `NormalizedBBox`, not just `BBoxBase`
bbox2 = bbox.move(1, 2)

更安全的互斥锁

Rust中的互斥锁和锁通常在一个非常漂亮的接口后面提供,有两个好处:

当你锁定互斥量时,你会得到一个保护对象,它会在互斥量被销毁时自动解锁,利用古老的RAII机制:

{
  let guard = mutex.lock(); // locked here
  ...
} // automatically unlocked here

这意味着你不会意外地忘记解锁互斥体。C++ 中也常用非常相似的机制,尽管不带保护对象的显式lock/unlock接口也可用于std::mutex,这意味着它们仍然可以被错误使用。

受互斥量保护的数据直接存储在互斥量(结构)中。使用这种设计,如果不实际锁定互斥体就不可能访问受保护的数据。您必须先锁定互斥量才能获得守卫,然后使用守卫本身访问数据:

let lock = Mutex::new(41); // Create a mutex that stores the data inside
let guard = lock.lock().unwrap(); // Acquire guard
*guard += 1; // Modify the data using the guard

这与主流语言(包括Python)中常见的互斥锁API形成鲜明对比,其中互斥锁和它保护的数据是分开的,因此你很容易忘记在访问数据之前实际锁定互斥锁:

mutex = Lock()

def thread_fn(data):
    # Acquire mutex. There is no link to the protected variable.
    mutex.acquire()
    data.append(1)
    mutex.release()

data = []
t = Thread(target=thread_fn, args=(data,))
t.start()

# Here we can access the data without locking the mutex.
data.append(2)  # Oops

虽然我们无法在Python中获得与在Rust中获得的完全相同的好处,但并非全部都失去了。Python锁实现了上下文管理器接口,这意味着你可以在块中使用它们with以确保它们在作用域结束时自动解锁。通过一点努力,我们可以走得更远:

import contextlib
from threading import Lock
from typing import ContextManager, Generic, TypeVar

T = TypeVar("T")

# Make the Mutex generic over the value it stores.
# In this way we can get proper typing from the `lock` method.
class Mutex(Generic[T]):
  # Store the protected value inside the mutex 
  def __init__(self, value: T):
    # Name it with two underscores to make it a bit harder to accidentally
    # access the value from the outside.
    self.__value = value
    self.__lock = Lock()

  # Provide a context manager `lock` method, which locks the mutex,
  # provides the protected value, and then unlocks the mutex when the
  # context manager ends.
  @contextlib.contextmanager
  def lock(self) -> ContextManager[T]:
    self.__lock.acquire()
    try:
        yield self.__value
    finally:
        self.__lock.release()

# Create a mutex wrapping the data
mutex = Mutex([])

# Lock the mutex for the scope of the `with` block
with mutex.lock() as value:
  # value is typed as `list` here
  value.append(1)

使用这种设计,你只能在实际锁定互斥锁后才能访问受保护的数据。显然,这仍然是Python,因此你仍然可以打破不变量——例如,通过在互斥量之外存储另一个指向受保护数据的指针。但是除非你的行为是敌对的,否则这会使Python中的互斥接口使用起来更安全。

不管怎样,我确信我在我的Python代码中使用了更多的“稳健模式”,但目前我能想到的就是这些。如果你有类似想法的一些示例或任何其他评论,请告诉我。

  1. 公平地说,如果你使用某种结构化格式(如 reStructuredText),文档注释中的参数类型描述可能也是如此。在那种情况下,类型检查器可能会使用它并在类型不匹配时警告你。但是,如果你无论如何都使用类型检查器,我认为最好利用“本机”机制来指定类型——类型提示。

  2. aka discriminated/tagged unions, sum types, sealed classes, etc.  

  3. 是的,除了这里描述的,新类型还有其他用例,别再对我大喊大叫了。

  4. 这被称为typestate 模式。

  5. 除非你努力尝试,例如手动调用魔术__exit__方法。

 

基于51单片机的智能花盆灌溉系统设计与实现 本文详细介绍了基于51单片机的智能花盆灌溉系统设计与实现,通过DS18B20温度传感器、土壤湿度传感器和LCD1602显示屏等硬件模块,结合精确的算法控制,实现自动监测与灌溉。系统解决了传统灌溉依赖定时器、缺乏可视化界面的问题,适合上班族、园艺爱好者和植物园工作人员使用,有效提升植物养护效率。 阅读详情

相关推荐

Vue 实现华视身份证读卡器功能

在www.downcc.com下载“华视cvr100驱动”安装程序; 华视电子官网有相应的文档说明和程序下载 一、服务安装 选择安装目录: 启动读卡服务: 安装成功会有如图所示: 注意事项: 安装开发包必须使用管理员权限,因为windows服务和读卡器设备驱动程序均需要管理员权限,否则会安装失败。 win10需要修改组策略才有管理员权限。 二、服务的使用 打开浏览器,输入地址http://127.0.0.1:19196/mainpage,回车 点击openDevice: 点.

zyqgt960812的博客 6503

python中的locals内置函数

python中的locals内置函数 1. locals的源码 def locals() -> dict[str, Any]: ... (function) locals: () -> dict[str, Any] Return a dictionary containing the current scope's local variables. NOTE: Whether or not updates to this dictionary will affect name lookups

belong_to_you的博客 959

dell r630 配置raid_DELL服务器RAID配置图文教程

名称解释:Disk Group:磁盘组,这里相当于是阵列,例如配置了一个RAID5,就是一个磁盘组VD(Virtual Disk): 虚拟磁盘,虚拟磁盘可以不使用阵列的全部容量,也就是说一个磁盘组可以分为多个VDPD(Physical Disk): 物理磁盘HS:Hot Spare 热备Mgmt:管理【一】创建逻辑磁盘1、按照屏幕下方的虚拟磁盘管理器提示,在VD Mgmt菜单(可以通过CTRL+P...

weixin_33253741的博客 3510

专属编程笔记

在软件开发中,Utils(或Utilities)目录通常用于存放一些。这些工具类或函数为整个应用程序或多个模块提供便利的功能支持,使得代码更加模块化、易于维护和重用。UtilsUtils目录的设计目的在于避免代码重复,并确保在整个项目中以一致的方式处理共通任务。这样做可以提高开发效率,降低维护成本,并确保代码的整洁性和可读性。

u014397365的博客 1897

OpenCV实战:用C++和Python实现亚像素边缘检测(附完整代码)

本文详细介绍了使用OpenCV实现亚像素边缘检测的技术,通过C++和Python双语言代码解析,提升工业测量和医学影像中的检测精度。文章涵盖算法原理、核心实现步骤及工业应用案例,帮助开发者掌握亚像素精度处理技术,解决高精度场景中的边缘检测难题。

beer8的博客 376

缠论 公式 自动画线 画笔 中枢 买卖点 源代码

【代码】缠论 公式 自动画线 画笔 中枢 买卖点 源代码。

weixin_40835859的博客 2万+

【python】数据类型的注释

algo (str)algo算法名称algoenv_idAnyoptionalNone参数值的类型可以是任意类型(AnyNonealgo字符串env_id一个字典可选,默认为None一个字典可选,默认为None。

wq6qeg88的博客 757

手把手教你学Simulink——逆变器场景:基于PR控制器的单相逆变器并网控制策略仿真

摘要:本文详细介绍基于PR控制器的单相逆变器并网控制策略的Simulink仿真实现。首先分析PR控制器在单相逆变应用中的优势,然后逐步讲解模型搭建过程,包括主电路设计(直流电源、全桥逆变器、L滤波器)、控制回路实现(参考电流生成、坐标变换、PR控制器设计)以及关键参数设置。通过仿真验证了PR控制器对电网频率的精确跟踪能力,并网电流THD<3%。最后提出优化建议,如加入状态观测器、自适应参数整定等改进方向。该建模方法为可再生能源系统中的逆变器控制提供了有效验证手段。

MHD0815的博客 497

一些有用的小工具

  本文将介绍一些有用的小工具,它们能够帮助我们提高工作效率。本文将介绍笔者在近一周发现的三个小工具,写文章以做记录,希望能对大家有所帮助。   本文将介绍的三个小工具如下: 在Sublime Text中支持Python运行; 在Mac电脑的iTerm2中支持查看图片; Python的typing模块介绍。   详细内容介绍如下。 在Sublime Text中支持Python运行   在平时工...

山阴少年 490

SAP S/4HANA Public Cloud 的 Development Tenant 深度解析

在传统的 SAP On-Premise 版本中,企业通常拥有完整的开发、测试、生产环境,开发人员可以直接访问 ABAP 代码,进行深度定制。然而,S/4HANA Public Cloud 采用了标准化的 SaaS 模型,不允许用户直接修改核心代码。因此,SAP 提供了一种新的开发方式,即 Development Tenant,允许企业在不破坏核心系统稳定性的前提下,实现个性化扩展。Development Tenant 主要用于 SAP S/4HANA Public Cloud 的扩展开发。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 849

5、数据库基础概念与数据建模全解析

本文深入解析了数据库的基础概念,包括函数依赖、决定因素和多值依赖等关键依赖关系,同时探讨了数据建模的重要方法论,如IDEF1X、信息工程和陈方法。此外,还介绍了SQL Server管理工作室的图表功能,帮助用户进行数据库的可视化设计。通过本文,读者将掌握数据库设计的核心知识,为构建高效、可扩展的数据库系统奠定基础。

81

python中的str、list、tuple、dict

01 str 字符串 str是不可变的,双引号或者单引号中的数据,就是字符串 str可进行的操作:下表索引、切片 下标索引 name = &quot;abcdef&quot; print(name[0]) print(name[1]) 切片 切片的语法:[ 起始:结束:步长 ] 注意:选取的区间属于左闭右开型,即从”起始”位开始,到”结束”位的前一位结束(不包含结束位本身) name = 'abcde...

windows下搭建virtualenv、virtualenvwrapper虚拟环境 1444

2万字大厂面经:小米嵌入式面试题及参考答案(持续更新)

BIOS (Basic Input/Output System) 是一种存储在ROM中的固件程序,它为计算机提供了最基础的硬件初始化和服务。BIOS主要存在于传统的x86架构计算机中。嵌入式系统的启动流程取决于具体的硬件平台和所采用的操作系统。复位阶段:当系统通电或复位时,CPU进入复位状态。硬件初始化:CPU和必要的硬件设备(如内存控制器)被初始化。加载引导加载程序:引导加载程序(如U-Boot)被加载到内存中。执行引导加载程序:引导加载程序开始执行,初始化更多的硬件设备,并检测存储介质。

大模型大数据攻城狮的专栏 2529

告别命令行!在VSCode里像写Python一样玩转Rust:从Hello World到单步调试的完整指南

本文为Python开发者提供了一份在VSCode中全图形化开发Rust的完整指南,涵盖从Hello World到单步调试的全流程。通过安装Rust Extension Pack和CodeLLDB扩展,开发者可以像使用Python一样便捷地创建项目、编写代码和调试程序,无需接触命令行。文章特别强调了rust-analyzer的实时错误检查和CodeLLDB的调试体验,帮助开发者快速上手Rust开发。

weixin_30580341的博客 314

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

“用过 Rust 后,我写 Python 的方法都变了!”

近年来,Rust 以其安全性出名,逐渐被各大科技巨头所拥抱——那么,其他主流语言是否可以参考Rust 的编程思想呢?本文作者以 Python 为例,做了一番尝试。原文链接:https://kobzol.github.io/rust/python/2023/05/20/writing-python-like-its-rust.html未经允许,禁止转载!作者 | Jakub Beránek译者 |...

优快云资讯 3632

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

初探: 通过pyo3用rust为python写扩展

众所周知,python性能比较差,尤其在计算密集型的任务当中,所以机器学习领域的算法开发,大多是将python做胶水来用,他们会在项目中写大量的C/C++代码然后编译为so动态文件供python加载使用。那么时至今日,对于不想学习c/c++的朋友们,rust可以是一个不错的替代品,它有着现代化语言的设计和并肩c/c++语言的运行效率。本文简单介绍使用rust为python计算性质的代码做一个优化,使用库为python写一个扩展供其调用,咱们下面开始,来看看具体的过程和效率的提升。

非马梦衢的博客 1646

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

rust让你的python飞起来!

Note: 本文作为入门教程,抛砖引玉,帮你初步了解如何使用rust为python写扩展模块,涉及从头到尾的详细步骤,基于此,剩下的只有深入rust,才能做得更好。众所周知,python性能比较差,尤其在计算密集型的任务当中,所以机器学习领域的算法开发,大多是将python做胶水来用,他们会在项目中写大量的C/C++代码然后编译为so动态文件供python加载使用。那么时至今日,对于不想学习c/c++的朋友们,rust可以是一个不错的替代品,它有着现代化语言的设计和并肩c/c++语言的运行效率。

Everly_的博客 1013

BepInEx 插件加载机制深度解析:如何让插件按正确顺序加载

BepInEx 是面向 Unity 与 .NET 游戏的插件框架,负责在游戏启动时完成插件 DLL 的发现、排序与加载。本文围绕“插件没被加载”这一真实问题,讲清其发现、依赖解析与加载流程,并演示如何用元数据属性和日志定位故障。 ## 场景:刚放进去的插件为什么没被加载 你把新构建的插件 dll 丢进游戏的 BepInEx/plugins 目录,重启游戏,画面正常但功能没生效。翻开 LogOu

gitblog_00957的博客 876

用 Rust 写爬虫真的比 Python 快 10 倍?实测告诉你

本文对比了Python和Rust在网络爬虫中的性能表现。测试采用相同条件爬取1000篇文章,Python(异步)平均耗时28.6秒,内存占用128MB;Rust仅需3.2秒,内存35MB,性能接近Python的9倍。Rust的优势在于无GC、原生异步支持和编译优化,但开发效率低于Python且生态不够成熟。结论:小型项目推荐Python开发效率高,大规模高并发场景则Rust性能优势明显。技术选型应结合实际需求,没有绝对优劣之分。

weixin_41943766的博客 2422

Rust std fs 比 Python 慢!真的吗!?

总的来说,这个问题并非与软件有关。由于 AMD 的一个错误,Python 在性能上超过了 C/Rust。(我终于可以好好睡觉了。然而,我们的用户仍然需要面对这个问题。不幸的是,像FSRM这样的功能将会被实现在ucode中,我们别无选择只能等待 AMD 的回应。另一种可能的解决方案是不使用FSRM或者提供一个标志来禁用它。Rust 开发者可能会考虑切换到以提高性能 ,即使没有 AMD CPU Bug 存在,这也是一个好主意!

Databend的博客 1419

Flapigen Python扩展开发:让Python应用享受Rust的高性能

你是否曾经希望Python应用能够拥有Rust语言的高性能和内存安全性?🤔 今天,我将为你介绍一个强大的工具——Flapigen,它能够轻松地将Rust代码集成到Python中,让你的Python应用获得Rust级别的性能提升!🚀 ## 什么是Flapigen? Flapigen是一个**Rust语言绑定生成器**,专门用于连接Rust程序或库与其他编程语言。它能够自动生成跨语言接口代码,

gitblog_00915的博客 838

这个Python库让Rust开发者直呼相见恨晚

这个库的名字叫Pydantic,它是一个数据验证库,使用Python的类型提示进行数据验证。id: intname: stremail: strraise ValueError('邮箱必须包含@符号')return vuser = User(id=1, name="张三", email="zhangsan@example.com")# user = User(id=1, name="张三", email="zhangsanexample.com") # 验证错误。

no1coder的博客 1万+

rust: 写CTP策略中的几个难点

写CTP策略,最好的用原生C++写;其次是C++或Java;个人不推荐用python之类(有争议)。 从rust的语言本身来看,虽然是比较适合写交易策略的。 但可惜到目前,并没有人开源。 一方面原因是rust还很小众,群众基础不够;另外一方面是,rust的学习曲线很陡,让人生畏,水平不高,单独弄还真有难度。 简单总结一下,有几方面的难度: 1、CTP 的C++接口封装 这个难度存在两方面,一方面对...

代码过客 3651

在 Python 里用 Rust:让 Python 再次强大!

全栈AI·探索:涵盖动效、React Hooks、Vue 技巧、LLM 应用、Python 脚本等专栏,案例驱动实战学习,点击二维码了解更多详情。你大概听过它的名号,但未必摸清了它的“脾气”。我有一支技术全面、经验丰富的小型团队,专注高效交付中等规模外包项目,有需要外包项目的可以联系我。本文的目的,是让你相信把 Rust 与 Python 结合并不难。注:为讲解直观,本文代码偏教学取向,并非最佳实践或完美 API 设计范式。结构体有点像 Python 的 dataclass,但能做的更复杂。

粉丝们务必加入微信粉丝群 516

从 Python 到 Go 再到 Rust,谁是后端开发之王?

【优快云 编者按】作为一种简单易上手的面向对象的动态类型语言,Python 在近两年的技术圈中,可谓是炙手可热;而另一种素有 Google “亲儿子”之称的 Go 语言,被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言,其自从诞生起,就备受业界关注;最后来自 Mozilla 开发的系统编程语言 Rust,在安全性上表现尤为突出,且在过去三年间连续获得了...

优快云资讯 1万+

Rust与Python:为什么Rust可以取代Python

来自我的博客:https://blog.zhangbing.site/2021/02/23/rust-vs-python-why-rust-could-replace-python/ 在本指南中,我们将比较Rust和Python编程语言。我们将讨论每种情况下的适用用例,回顾使用Rust与Python的优缺点,并说明为什么Rust可能会取代Python。 我将介绍以下内容: 什么是Rust? 什么是Python? 何时使用Rust 何时使用Python 为什么Rust可以取代Python 什么是Ru..

前端全栈开发者 6950
上一篇: Spring中毒太深,离开Spring居然连最基本的接口都不会写了
下一篇: 4个月16起!GitHub崩溃不止!
IT界那些事儿
博客等级 码龄6年 2万+粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值