Go方法特性详解:简单性和高效性的充分体现
一、简介
在软件开发的世界里,理解并掌握编程语言的各种特性是至关重要的。Go(又称Golang)作为一种现代的编程语言,以其简洁的语法和出色的性能吸引了大量的开发者。然而,Go的方法(Methods)这一核心特性却常常被误解或忽视。这不仅会影响代码的质量,还可能导致在更复杂的系统或框架中遇到各种问题。
本文旨在深入剖析Go中方法的概念和特性,同时提供实战应用的例子和最佳实践,以帮助你更全面、更深入地理解这一重要主题。文章将首先介绍Go中方法与普通函数的不同之处,然后深入探讨方法的各种特性,包括但不限于接收者类型、值接收者与指针接收者的不同,以及如何利用方法进行更高级的编程技巧。
我们还将通过一系列细致入微的代码示例来具体展示这些概念和特性如何在实际开发中应用,包括JSON序列化、自定义数据结构的排序等实用场景。此外,考虑到方法在大规模或高性能应用中的重要性,本文还将对比分析不同类型接收者在性能方面的差异,并提供优化建议。
本文适合有一定Go语言基础,并希望深化对Go方法特性了解的读者。无论你是希望提高代码质量,还是在寻找提升系统性能的方案,这篇文章都将为你提供有价值的信息和实用的技巧。
二、基础概念
在深入探讨Go语言中方法特性的各种高级用法之前,我们首先需要弄清楚几个基础概念。理解这些概念不仅能帮助我们更好地理解后续的高级话题,而且也是编写健壮、高效代码的基础。
什么是方法
在Go语言中,方法是一种特殊类型的函数,它是附加在特定类型上的。这意味着,这个特定类型的变量就可以调用该方法。
type Circle struct {Radius float64
}func (c Circle) Area() float64 {return 3.14159 * c.Radius * c.Radius
}// 使用示例
var myCircle Circle
myCircle.Radius = 5
fmt.Println("Area of circle:", myCircle.Area())
在这个例子中,Area 是一个绑定在 Circle 类型上的方法。你可以创建一个 Circle 类型的变量 myCircle,并通过 myCircle.Area() 来调用这个方法。
方法与函数的区别
尽管Go语言中的方法在形式上看起来像是函数,但两者还是有几个关键区别。
- 接收者: 方法定义中的第一个参数被称为接收者,它定义了该方法与哪种类型关联。
- 调用方式: 方法需要通过具体的变量来进行调用,而函数则可以直接调用。
// 函数
func Add(a, b int) int {return a + b
}// 方法
type Integer intfunc (a Integer) Add(b Integer) Integer {return a + b
}// 使用示例
result := Add(1, 2) // 函数调用var a Integer = 1
var b Integer = 2
result = a.Add(b) // 方法调用
在这个例子中,Add 函数和 Integer 类型的 Add 方法实现了同样的功能,但它们的定义和调用方式有所不同。
方法的接收者
Go语言允许两种类型的接收者:值接收者和指针接收者。
- 值接收者: 在调用方法时会传递一个接收者的副本。
- 指针接收者: 在调用方法时会传递接收者变量的地址。
这两者有各自的优缺点和适用场景,但选择哪种接收者类型主要取决于是否需要在方法中修改接收者或关注性能优化。
// 值接收者
func (c Circle) Diameter() float64 {return 2 * c.Radius
}// 指针接收者
func (c *Circle) SetRadius(r float64) {c.Radius = r
}// 使用示例
var c Circle
c.Radius = 5
fmt.Println("Diameter:", c.Diameter()) // 值接收者调用c.SetRadius(10) // 指针接收者调用
fmt.Println("New Radius:", c.Radius)
在这个例子中,Diameter 是一个值接收者的方法,它返回圆的直径。SetRadius 是一个指针接收者的方法,用于设置圆的半径。
以上就是Go语言中方法基础概念的细致解析和实例展示。在理解了这些基础知识之后,我们可以更有信心地探索更多高级的方法使用场景和性能优化技巧。
三、Go方法的定义和声明
了解了Go方法的基础概念后,接下来我们将更详细地探讨如何在Go语言中定义和声明方法。虽然这部分内容看似基础,但其实包含了很多易被忽视的细节和陷阱。
方法的基础声明
在Go中,方法的基础声明非常直观。和函数相似,方法也有名称、参数列表和返回值,但不同之处在于方法还有一个额外的“接收者”参数。
// 方法定义示例
func (receiver ReceiverType) MethodName(arg1 Type1, arg2 Type2) ReturnType {// 方法体
}
// 实际例子
type Square struct {SideLength float64
}func (s Square) Area() float64 {return s.SideLength * s.SideLength
}// 使用示例
var mySquare Square
mySquare.SideLength = 4
fmt.Println("Area of square:", mySquare.Area())
在这个例子中,我们定义了一个名为Square的结构体和一个名为Area的方法,这个方法用于计算正方形的面积。
方法与接收者类型
Go语言允许为任何用户自定义的类型(包括结构体和别名类型)添加方法,但不允许为内建类型或从其他包导入的类型添加方法。
// 为内建类型添加方法(错误的做法)
func (i int) Double() int {return i * 2
}// 为别名类型添加方法(正确的做法)
type MyInt intfunc (i MyInt) Double() MyInt {return i * 2
}
在这个例子中,我们尝试为内建类型int添加一个Double方法,这是不允许的。但我们可以定义一个别名类型MyInt,并为它添加方法。
值接收者和指针接收者
我们之前已经简单讨论过值接收者和指针接收者,但在方法定义中这两种接收者有哪些不同呢?
- 值接收者会创建接收者的一个副本,因此在方法内部对接收者的任何修改都不会影响原始值。
- 指针接收者则是传递接收者的内存地址,因此在方法内部对接收者的修改会影响原始值。
// 值接收者
func (s Square) SetSideLength(val float64) {s.SideLength = val
}// 指针接收者
func (s *Square) SetSideLengthPtr(val float64) {s.SideLength = val
}// 使用示例
var mySquare Square
mySquare.SideLength = 4mySquare.SetSideLength(5)
fmt.Println("Side Length after value receiver:", mySquare.SideLength) // 输出 4mySquare.SetSideLengthPtr(5)
fmt.Println("Side Length after pointer receiver:", mySquare.SideLength) // 输出 5
这个例子通过SetSideLength和SetSideLengthPtr两个方法展示了值接收者和指针接收者在修改接收者值方面的不同。
重载和方法名冲突
需要注意的是,Go语言不支持传统意义上的方法重载,也就是说,不能有两个同名但参数不同的方法。
同时,Go也不允许一个结构体同时拥有值接收者和指针接收者的同名方法。
type MyStruct struct {Field int
}func (m MyStruct) MyMethod() {fmt.Println("Method with value receiver.")
}func (m *MyStruct) MyMethod() { // 编译错误fmt.Println("Method with pointer receiver.")
}
这样做会导致编译错误,因为Go会无法确定在特定情况下应该调用哪一个。
四、Go方法的特性
Go语言的方法虽然在表面上看似简单,但实际上隐藏了许多强大和灵活的特性。这些特性让Go方法不仅仅是一种对函数的简单封装,而是成为了一种强大的抽象机制。在本节中,我们将详细探讨这些特性。
方法值与方法表达式
在Go中,方法不仅仅可以通过接收者来调用,还可以被赋值给变量或者作为参数传递,这是通过方法值和方法表达式实现的。
type MyInt intfunc (i MyInt) Double() MyInt {return i * 2
}// 使用示例
var x MyInt = 4
doubleFunc := x.Double // 方法值result := doubleFunc() // 输出 8
在这个例子中,x.Double 是一个方法值,它被赋值给了变量 doubleFunc,之后你可以像调用普通函数一样调用它。
方法的组合与嵌入
Go没有提供传统的面向对象编程语言中的类和继承机制,但通过结构体嵌入(Embedding)和方法组合,你可以轻易地实现复用和组合。
type Shape struct {Name string
}func (s Shape) Display() {fmt.Println("This is a", s.Name)
}type Circle struct {Shape // 嵌入ShapeRadius float64
}// 使用示例
c := Circle{Shape: Shape{Name: "Circle"}, Radius: 5}
c.Display() // 输出 "This is a Circle"
在这里,Circle 结构体嵌入了 Shape 结构体,从而也“继承”了其 Display 方法。
方法的可见性
方法的可见性遵循与字段和函数相同的规则。如果一个方法的名称以大写字母开头,那么该方法在包外也是可见的;反之,则只在包内可见。
type myType struct {field int
}// 包内可见
func (m myType) privateMethod() {fmt.Println("This is a private method.")
}// 包外可见
func (m myType) PublicMethod() {fmt.Println("This is a public method.")
}
这一点对于封装特别重要,因为你可以控制哪些方法应该对外暴露,哪些应该隐藏。
方法的覆盖
当一个结构体嵌入了另一个拥有某方法的结构体,嵌入结构体可以提供一个同名方法来“覆盖”被嵌入结构体的方法。
func (c Circle) Display() {fmt.Println("This is not just a shape, but specifically a circle.")
}// 使用示例
c := Circle{Shape: Shape{Name: "Circle"}, Radius: 5}
c.Display() // 输出 "This is not just a shape, but specifically a circle."
在这个例子中,Circle 提供了一个新的 Display 方法,从而覆盖了 Shape 的 Display 方法。
方法集
一个类型的方法集是该类型能调用的所有方法的集合。对于值类型和指针类型,这个集合是不同的。这一点在接口的实现和类型转换时尤为重要。
type Cube struct {SideLength float64
}func (c *Cube) Volume() float64 {return c.SideLength * c.SideLength * c.SideLength
}// 使用示例
var myCube *Cube = &Cube{SideLength: 3}
var cubeVolume float64 = myCube.Volume()
在这个例子中,Volume 方法只能通过一个 Cube 指针来调用,因为它定义时使用了指针接收者。
五、实战应用
在理解了Go方法的各种特性之后,我们将在这一部分探讨如何在实际应用中有效地使用它们。这里将通过几个具体的场景和示例来展示Go方法特性的实用性。
使用方法值进行事件处理
方法值特性在事件处理模型中非常有用。假设我们有一个Web服务器,我们想对不同类型的HTTP请求执行不同的逻辑。
type Handler struct {route map[string]func(http.ResponseWriter, *http.Request)
}func (h *Handler) AddRoute(path string, f func(http.ResponseWriter, *http.Request)) {h.route[path] = f
}func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {if f, ok := h.route[r.URL.Path]; ok {f(w, r)} else {http.NotFound(w, r)}
}// 使用示例
h := &Handler{route: make(map[string]func(http.ResponseWriter, *http.Request))}
h.AddRoute("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, world!")
})
http.ListenAndServe(":8080", h)
这里,ServeHTTP 是一个方法值,它会根据不同的路由调用不同的函数。
利用嵌入和方法覆盖实现策略模式
策略模式是一种设计模式,允许算法的行为在运行时动态更改。通过Go的嵌入和方法覆盖特性,我们可以轻易地实现这一模式。
type Sorter struct{}func (s *Sorter) Sort(arr []int) {fmt.Println("Default sort algorithm")
}type QuickSorter struct {Sorter
}func (qs *QuickSorter) Sort(arr []int) {fmt.Println("Quick sort algorithm")
}// 使用示例
s := &Sorter{}
s.Sort(nil) // 输出 "Default sort algorithm"qs := &QuickSorter{}
qs.Sort(nil) // 输出 "Quick sort algorithm"
在这个例子中,QuickSorter 继承了 Sorter 的所有方法,并通过覆盖 Sort 方法来提供一个不同的实现。
利用方法集实现接口
方法集是确定类型是否满足接口的关键因素。例如,考虑一个Drawable接口:
type Drawable interface {Draw()
}type Circle struct {Radius float64
}func (c *Circle) Draw() {fmt.Println("Drawing a circle.")
}func DrawAllShapes(shapes []Drawable) {for _, s := range shapes {s.Draw()}
}// 使用示例
shapes := []Drawable{&Circle{Radius: 5}}
DrawAllShapes(shapes) // 输出 "Drawing a circle."
在这里,由于Circle的方法集包含了Draw方法,因此它满足了Drawable接口。
六、性能考量
在使用Go的方法特性时,性能是一个不可忽视的重要方面。本节将详细讨论与Go方法性能相关的各种考量,并通过实例来解释。
方法调用与函数调用的开销
首先,理解方法调用与普通函数调用之间的性能差异是重要的。
func FunctionAdd(a, b int) int {return a + b
}type Adder struct {a, b int
}func (adder Adder) MethodAdd() int {return adder.a + adder.b
}// 使用示例
func BenchmarkFunctionAdd(b *testing.B) {for i := 0; i < b.N; i++ {_ = FunctionAdd(1, 2)}
}func BenchmarkMethodAdd(b *testing.B) {adder := Adder{1, 2}for i := 0; i < b.N; i++ {_ = adder.MethodAdd()}
}
经过基准测试,你会发现这两者之间的性能差异通常非常小,并且通常不是性能瓶颈。
指针接收者与值接收者
使用指针接收者和值接收者会产生不同的性能影响,尤其是当结构体比较大或者涉及到修改操作时。
type BigStruct struct {data [1 << 20]int
}func (b *BigStruct) PointerReceiverMethod() int {return b.data[0]
}func (b BigStruct) ValueReceiverMethod() int {return b.data[0]
}
使用指针接收者通常更快,因为它避免了值的拷贝。
方法内联
方法内联是编译器优化的一个方面,它会影响方法调用的性能。简短且被频繁调用的方法更可能被编译器内联。
func (b *BigStruct) LikelyInlined() int {return b.data[0]
}func (b *BigStruct) UnlikelyInlined() int {sum := 0for _, v := range b.data {sum += v}return sum
}
LikelyInlined 方法由于其简短和直接,更可能被编译器内联,从而提供更好的性能。
延迟方法与即时方法
Go提供了延迟执行方法(defer)的特性,但这通常会带来额外的性能开销。
func DeferredMethod() {defer fmt.Println("This is deferred.")fmt.Println("This is not deferred.")
}
除非必要(例如,进行资源清理等),否则避免使用 defer 可以提高性能。
七、总结
在本文中,我们深入探讨了Go语言中方法的各种特性和应用,从基础概念和定义,到高级用法和性能考量,每个方面都进行了详细的剖析和实例演示。但在所有这些技术细节之外,有一点可能更为重要:方法是Go编程哲学的一个微观体现。
Go强调简单性和高效性,这一点在其方法设计上得到了充分体现。与其他编程语言相比,Go没有过多的修饰和冗余,每一个特性都是精心设计的,旨在解决实际问题。例如,Go的方法值和方法表达式提供了对函数一等公民特性的有限但高效的支持。这反映了Go的设计哲学,即提供必要的灵活性,同时避免增加复杂性。
同样,在性能考量方面,Go方法的设计也展示了其对实用性和性能的均衡关注。从编译器优化(如方法内联)到运行时效率(如指针接收者和值接收者的不同用法),每一个细节都反映了这一点。
但最终,理解Go方法的关键不仅仅在于掌握其语法或记住各种“最佳实践”,而更在于理解其背后的设计哲学和目标。只有这样,我们才能更加明智地运用这些工具,写出更加优雅、高效和可维护的代码。
相关文章:
Go方法特性详解:简单性和高效性的充分体现
一、简介 在软件开发的世界里,理解并掌握编程语言的各种特性是至关重要的。Go(又称Golang)作为一种现代的编程语言,以其简洁的语法和出色的性能吸引了大量的开发者。然而,Go的方法(Methods)这一…...
Cesium Vue(四)— 物体(Entity)的添加与配置
1. 添加标签和广告牌 // 添加文字标签和广告牌var label viewer.entities.add({position: Cesium.Cartesian3.fromDegrees(113.3191, 23.109, 750),label: {text: "广州塔",font: "24px sans-serif",fillColor: Cesium.Color.WHITE,outlineColor: Cesium.…...
洗地机哪个好用?2023年洗地机推荐指南
说到提高家庭幸福生活的家电,洗地机肯定是少不了的,特别对于现在快节奏的生活来说,高效率的解决家务活,而且能够大幅度的提高生活质量。在市场上,消费者面临着选择合适洗地机的难题,因为有各种型号、功能和…...
螺旋矩阵(C++解法)
题目 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 示例 1: 输入:matrix [[1,2,3],[4,5,6],[7,8,9]] 输出:[1,2,3,6,9,8,7,4,5]示例 2: 输入:matrix [[…...
【Java 进阶篇】深入了解 Bootstrap 栅格系统
在网页开发中,创建响应式的布局是至关重要的,因为不同设备和屏幕尺寸需要不同的布局来呈现内容。Bootstrap 提供了一个强大的栅格系统,使开发者能够轻松创建适应不同屏幕的网页布局。本文将深入介绍 Bootstrap 栅格系统,面向初学者…...
Gradle中的buildScript代码块
PS: 在build script中的task apply plugin: spring-boot 需要 classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE") apply plugin: com.moowork.gulp 需要classpath com.moowork.gradle:gradle-gulp-plugin:0.10 在编写Gradle脚本的时…...
Spring boot 集成 xxl-job
文章目录 xxl-job 简介引入xxl-job依赖配置xxl-job config添加properties文件配置BEAN模式(方法形式)步骤一:执行器项目中,开发Job方法:步骤二:调度中心,新建调度任务 xxl-job 简介 官网:https:…...
uni-app通过 vuedraggable 创建上下拖动排序组件
我们右键项目 选择 使用命令行窗口打开所在目录 然后 在终端中输入 npm install vuedraggable --save导入 vuedraggable 然后组件编写代码如下 <template><view class"container"><draggable v-model"list" :options"dragOptions&…...
Android高版本读取沙盒目录apk解析安装失败解决方案
bug场景: 应用内升级下载apk完成后安装,vivo(Android13)手机会报解析包错误,7.0及以上的手机是没问题的。开始以为是v1,v2签名问题导致的,但是我用浏览器下载下来的安装包是能够正确安装的。排除v1,v2签名的…...
ddns-go配合aliyun域名解析通过ipv6实现共享桌面
ddns-go配合aliyun域名解析通过ipv6实现共享桌面 前提: 必须拥有ipv6公网IP,测试IPv6 测试 (testipv6.cn) 如果是光猫拨号一点要选择ipv4和ipv6,同时要看光猫是否支持ipv6转发,如果不支持转发也不行,光猫不支持ipv6…...
C#WPF 应用Grid布局实现用户登录实例
本文介绍C#WPF Grid布局应用实例,通过用户登录实例演示掌握Grid布局用法。 目录 一、Grid面板介绍 二、用户登录实例 一、Grid面板介绍 网格面板是所有面板中最复杂但用途最广泛的面板。网格面板可用于设计复杂的用户界面,我们需要将多个元素以行和列的表格格式放置。 WP…...
RHEL 8.6 Kubespray 1.23.0 install kubernetes v1.27.5
文章目录 1. 预备条件2. download01 节点 安装 dockerdownload01 节点 介质下载下载 bastion01节点配置 yum 源bastion01 节点安装 docker5. 安装 docker insecure registrybastion01 部署 nginx 与 镜像入库13.1 配置 config.sh13.2 配置 setup-docker.sh13.3 配置 start-ngin…...
RGBD Salient Object Detection via Disentangled Cross-Modal Fusion
方法 HHA means “horizontal disparity, height above ground, and angle with gravity”.结构化上下文编码器{E R S _R^S RS,E D S _D^S DS},模态特定内容编码器{E R C _R^C RC,E D C _D^C DC} 体会 作者未提供代码...
VMware下linux中ping报错unknown host的解决办法
一、错误截图 二、解决办法 2.1 按照步骤查看本机虚拟IP 依次点击:【编辑】》【虚拟网络编辑器】,选中NET模式所属的行,就能看到子网地址。 比喻,我的子网地址是:192.168.18.0 那么,接下来要配置的linux…...
Macos数据库管理:Navicat Premium 中文
Navicat Premium提供了直观且易用的图形用户界面,使得操作更为便捷。Navicat Premium 中文支持多种数据库系统,如MySQL、MariaDB、Oracle、SQLite、PostgreSQL等,可以让用户在同一平台上管理不同类型的数据库。Navicat Premium拥有强大的数据…...
数据结构 - 7(Map和Set 15000字详解)
一: 二叉搜索树 1.1 二叉搜索树的概念 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所…...
windows 11 安装PHP8.2
环境说明 windows:windows 11 x64apache: Apache/2.4.43php :php-8.2.11 一.php 1、PHP下载 PHP For Windows: Binaries and sources Releases 注意: 1.要下载Thread Safe,否则没有php8apache2_4.dll这个文件;如果使用Apache作为服务器…...
计算机网络学习笔记(三):数据链路层(待更新)
目录 3.1 基本概念 3.1.1 数据链路和帧 3.1.2 三个基本问题 3.2 类型1:使用点对点信道的数据链路层(路由器) 3.2.1 点对点协议 PPP:特点 3.2.2 点对点协议 PPP:帧格式 3.2.3 点对点协议 PPP:工作状态 …...
hbase操作学习
1.namespace list_namespace 展示数据库 create_namespace 可以带属性名 属性值 create_namespace mydb,{author>hjp,ctime>2023-10-18}describe_namespace ‘库名’ 查看库的详细信息 alter_namespace ‘库名’ 修改表的详细信息 删除就是把method设置为unset dr…...
Nginx详细配置指南
nginx.conf配置 找到Nginx的安装目录下的nginx.conf文件,该文件负责Nginx的基础功能配置。 配置文件概述 Nginx的主配置文件(conf/nginx.conf)按以下结构组织: 配置块功能描述全局块与Nginx运行相关的全局设置events块与网络连接有关的设置http块代理…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
PydanticAI快速入门示例
参考链接:https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...
