Go方法特性详解:简单性和高效性的充分体现

本文深入探讨了Go语言中方法的各个方面,包括基础概念、定义与声明、特性、实战应用以及性能考量。文章充满技术深度,通过实例和代码演示,力图帮助读者全面理解Go方法的设计哲学和最佳实践。

关注作者,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。

目录

    • 一、简介
    • 二、基础概念
      • 什么是方法
      • 方法与函数的区别
      • 方法的接收者
    • 三、Go方法的定义和声明
      • 方法的基础声明
      • 方法与接收者类型
      • 值接收者和指针接收者
      • 重载和方法名冲突
    • 四、Go方法的特性
      • 方法值与方法表达式
      • 方法的组合与嵌入
      • 方法的可见性
      • 方法的覆盖
      • 方法集
    • 五、实战应用
      • 使用方法值进行事件处理
      • 利用嵌入和方法覆盖实现策略模式
      • 利用方法集实现接口
    • 六、性能考量
      • 方法调用与函数调用的开销
      • 指针接收者与值接收者
      • 方法内联
      • 延迟方法与即时方法
    • 七、总结

file

一、简介

在软件开发的世界里,理解并掌握编程语言的各种特性是至关重要的。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语言中的方法在形式上看起来像是函数,但两者还是有几个关键区别。

  1. 接收者: 方法定义中的第一个参数被称为接收者,它定义了该方法与哪种类型关联。
  2. 调用方式: 方法需要通过具体的变量来进行调用,而函数则可以直接调用。
// 函数
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语言允许两种类型的接收者:值接收者和指针接收者。

  1. 值接收者: 在调用方法时会传递一个接收者的副本。
  2. 指针接收者: 在调用方法时会传递接收者变量的地址。

这两者有各自的优缺点和适用场景,但选择哪种接收者类型主要取决于是否需要在方法中修改接收者或关注性能优化。

// 值接收者
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,并为它添加方法。

值接收者和指针接收者

我们之前已经简单讨论过值接收者和指针接收者,但在方法定义中这两种接收者有哪些不同呢?

  1. 值接收者会创建接收者的一个副本,因此在方法内部对接收者的任何修改都不会影响原始值。
  2. 指针接收者则是传递接收者的内存地址,因此在方法内部对接收者的修改会影响原始值。
// 值接收者
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

这个例子通过SetSideLengthSetSideLengthPtr两个方法展示了值接收者和指针接收者在修改接收者值方面的不同。

重载和方法名冲突

需要注意的是,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 方法,从而覆盖了 ShapeDisplay 方法。

方法集

一个类型的方法集是该类型能调用的所有方法的集合。对于值类型和指针类型,这个集合是不同的。这一点在接口的实现和类型转换时尤为重要。

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方法的具体应用,还能激发你对编程和软件设计更深层次的思考。这样,无论你是在构建小型应用,还是在设计庞大的云服务架构,都能做出更加明智和高效的决策。

关注作者,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业人士,上亿营收AI产品研发负责人。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/347442.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Angular封装高德地图组件实现输入框搜索,地图点击选地点

Angular封装高德地图组件实现输入框搜索,地图点击选地点(Angular17版本) 话不多说直接上代码 创建一个独立组件 html代码: <div style"position: relative;"><input #searchInput nz-input placeholder"请输入地址"/><div #mapContaine…

如何快速上手Python,成为一名数据分析师

如何快速上手Python&#xff0c;成为一名数据分析师 成为一名数据分析师需要掌握Python编程语言以及数据分析相关的知识和技能。以下是一些步骤和建议&#xff0c;帮助你快速上手Python并成为一名数据分析师&#xff1a; 学习Python基础知识&#xff1a;首先&#xff0c;你需要…

机器学习-聚类算法

1.有监督学习与无监督学习 有监督&#xff1a;在训练集中给的数据中有X和Y&#xff0c;根据这些数据训练出一组参数对预测集进行预测 无监督&#xff1a;在训练集中给的数据只有X没有Y&#xff0c;根据X数据找相似度参数来对预测集进行预测 2.数据间的相似度 2.1距离相似度…

前端 Web 与原生应用端 WebView 通信交互 - HarmonyOS Next

基于鸿蒙 HarmonyOS Next 与前端 Vue 通信交互相关小结; DevEco Studio NEXT Developer Preview2 Vue js 两端相互拟定好协议后,通过前端页面的点击事件,将所需的数据传输给原生移动端组件方法中,处理后将消息回传至前端. 根据官方文档的案例尝试,但没成功 ... 后经过几经尝试…

随笔——预处理详解

目录 前言预定义符号#define#define定义常量#define定义宏 带有副作用的宏参数宏替换的规则宏和函数的对比#和###运算符##运算符 命名约定#undef命令行定义条件编译头文件的包含包含方式嵌套包含 其他预处理指令完 前言 之前我们在《随笔——编译与链接》中对预处理作了大致的…

【ARM Cache 及 MMU 系列文章 6 -- Cache 寄存器 CTR_EL0 | CLIDR | CCSIDR | CSSELR 使用详解 1】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 Cache 常用寄存器Cache CSSELR 寄存器Cache CSSELR 使用场景Cache CSSELR 操作示例 Cache CLIDR 寄存器LoUU 介绍LoUU 使用 LoUIS 介绍CLIDR 使用 Cache CCSIDR 寄存器Cache CTR_EL0 C…

中科数安 |-公司办公透明加密系统,数据防泄漏软件

#数据防泄漏软件# 中科数安是一家专注于提供企业级数据防泄漏解决方案的公司&#xff0c;其办公透明加密系统是专为保护企业内部核心数据资料设计的。 PC地址&#xff1a;——www.weaem.com 该系统通过以下主要功能模块实现高效的安全防护&#xff1a; 文档透明加密&#xff1…

滴滴出行 大数据研发实习生【继任】

大数据研发实习生JD 职位描述 1、负责滴滴核心业务的数据建设&#xff0c;设计并打造适应滴滴一站式出行平台业务特点的数仓体系。 2、负责抽象核心业务流程&#xff0c;沉淀业务通用分析框架&#xff0c;开发数仓中间层和数据应用产品。 3、负责不断完善数据治理体系&#xff…

【数据结构】栈的应用

目录 0 引言 1 栈在括号匹配中的应用 2 栈在表达式求值中的应用 2.1 算数表达式 2.2 中缀表达式转后缀表达式 2.3 后缀表达式求值 3 栈在递归中的应用 3.1 栈在函数调用中的作用 3.2 栈在函数调用中的工作原理 4 总结 0 引言 栈&#xff08;Stack&#xff09;是一…

WPF视频学习-基础知识篇

1.简介WPF&#xff1a; C# 一套关于windows界面应用开发框架 2.WPF和winform的差别 &#xff0c;(WPF比较新) 创建新项目使用模板&#xff1a; WPF使用.xaml后缀&#xff0c;双击可查看操作界面和设置代码&#xff0c;其文件展开之后中有MainWindow.xaml.cs为程序交互逻辑。…

linux笔记8--安装软件

文章目录 1. PMS和软件安装的介绍2. 安装、更新、卸载安装更新ubuntu20.04更新镜像源&#xff1a; 卸载 3. 其他发行版4. 安装第三方软件5. 推荐 1. PMS和软件安装的介绍 PMS(package management system的简称)&#xff1a;包管理系统 作用&#xff1a;方便用户进行软件安装(也…

nginx mirror流量镜像详细介绍以及实战示例

nginx mirror流量镜像详细介绍以及实战示例 1.nginx mirror作用2.nginx安装3.修改配置3.1.nginx.conf3.2.conf.d目录下添加default.conf配置文件3.3.nginx配置注意事项3.3.nginx重启 4.测试 1.nginx mirror作用 为了便于排查问题&#xff0c;可能希望线上的请求能够同步到测试…

PyCharm QThread 设置断点不起作用

背景&#xff1a; 端午节回来上班第一天&#xff0c;不想干活&#xff0c;领导又再后面看着&#xff0c;突然想起一个有意思的问题&#xff0c;为啥我的程序在子进程QThread的子类里打的断点不好用呢&#xff1f;那就解决一下这个问题吧。 原因&#xff1a; 如果您的解释器上…

开发框架表单设计器办公效率高吗?

对于很多职场人来说&#xff0c;拥有一款可以提质、增效、降本的办公利器是大有裨益的。随着科技的进步和发展&#xff0c;低代码技术平台凭借可视化界面、易操作、好维护、高效率等多个优势特点&#xff0c;成为大众喜爱的办公利器。开发框架表单设计器是减少信息孤岛&#xf…

macbook本地部署 pyhive环境连接 hive用例

前言 公司的测试和生产环境中尚未提供基于Hive的客户端。若希望尝试操作Hive表&#xff0c;目前一个可行的方案是使用Python语言&#xff0c;通过借助pyhive库&#xff0c;您可以对Hive表进行各种操作。以下是一些示例记录供您参考。 一、pyhive是什么&#xff1f; PyHive是一…

从零到一建设数据中台(番外篇)- 数据中台UI欣赏

番外篇 - 数据中台 UI 欣赏 话不多说&#xff0c;直接上图。 数据目录的重要性&#xff1a; 数据目录是一种关键的信息管理工具&#xff0c;它为组织提供了一个全面的、集中化的数据资产视图。 它不仅记录了数据的存储位置&#xff0c;还详细描述了数据的结构、内容、来源、使…

细说ARM MCU的串口接收数据的实现过程

目录 一、硬件及工程 1、硬件 2、软件目的 3、创建.ioc工程 二、 代码修改 1、串口初始化函数MX_USART2_UART_Init() &#xff08;1&#xff09;MX_USART2_UART_Init()串口参数初始化函数 &#xff08;2&#xff09;HAL_UART_MspInit()串口功能模块初始化函数 2、串口…

批量申请SSL证书如何做到既方便成本又最低

假如您手头拥有1千个域名&#xff0c;并且打算为每一个域名搭建网站&#xff0c;那么在当前的网络环境下&#xff0c;您必须确保这些网站通过https的方式提供服务。这意味着&#xff0c;您将为每一个域名申请SSL证书&#xff0c;以确保网站数据传输的安全性和可信度。那么&…

面试-NLP八股文

机器学习 交叉熵损失&#xff1a; L − ( y l o g ( y ^ ) ( 1 − y ) l o g ( 1 − ( y ^ ) ) L-(ylog(\hat{y}) (1-y)log(1-(\hat{y})) L−(ylog(y^​)(1−y)log(1−(y^​))均方误差&#xff1a; L 1 n ∑ i 1 n ( y i − y ^ i ) 2 L \frac{1}{n}\sum\limits_{i1}^{n}…

【ai】openai-quickstart 配置pycharm工程

之前都是本地执行脚本【AI】指定python3.10安装Jupyter Lab环境为:C:\Users\zhangbin\AppData\Local\Programs\Python\Python310 参考之前创建的python工程 使用的是局部的私有的虚拟环境 pycharm给出的解释器 直接使用现有的,不new了 可以选择3.10 :可以选虚拟的: