Go再进阶:结构体、接口与面向对象编程

🚀 Go再进阶:结构体、接口与面向对象编程

大家好!在前两篇文章中,我们深入学习了Go语言的流程控制语句以及数组和切片的使用并且还对Go 语言的核心知识点进行了补充讲解,这些知识让我们能够编写出更为复杂和灵活的程序。

今天,我们将继续探索Go语言的强大特性,深入了解结构体、接口以及Go语言独特的面向对象编程方式。这些内容将帮助我们更好地组织和管理代码,构建大型的、可维护的应用程序。

一、结构体:自定义的数据类型

在实际编程中,我们常常需要将不同类型的数据组合在一起,形成一个逻辑上的整体。结构体(struct)就是Go语言提供的用于满足这种需求的工具,它允许我们创建自定义的数据类型。

1. 结构体的定义

结构体的定义使用 struct 关键字,基本格式如下:

type 结构体名称 struct {字段1 数据类型字段2 数据类型// 可以有更多的字段
}

示例:定义一个表示学生的结构体

package mainimport "fmt"// 定义学生结构体
type Student struct {Name  stringAge   intGrade float32
}

2. 结构体实例化与初始化

定义好结构体后,我们可以创建结构体的实例并进行初始化。

方式一:使用键值对初始化

func main() {student1 := Student{Name:  "小明",Age:   18,Grade: 3.5,}fmt.Printf("学生1: 姓名 %s, 年龄 %d, 成绩 %.2f\n", student1.Name, student1.Age, student1.Grade)
}

方式二:按照字段顺序初始化

func main() {student2 := Student{"小红", 17, 3.8}fmt.Printf("学生2: 姓名 %s, 年龄 %d, 成绩 %.2f\n", student2.Name, student2.Age, student2.Grade)
}

3. 访问结构体字段

通过实例变量和点号(.)操作符来访问结构体的字段。

func main() {student := Student{Name:  "小李",Age:   19,Grade: 3.6,}// 修改字段值student.Age = 20student.Grade = 3.7fmt.Printf("学生: 姓名 %s, 年龄 %d, 成绩 %.2f\n", student.Name, student.Age, student.Grade)
}

二、接口:定义行为的契约

接口(interface)是Go语言中一个非常重要的概念,它定义了一组方法的签名,但不包含方法的实现。接口为不同类型提供了一种统一的调用方式,使得代码更加灵活和可扩展。

1. 接口的定义

使用 interface 关键字定义接口,基本格式如下:

type 接口名称 interface {方法1(参数列表) 返回值列表方法2(参数列表) 返回值列表// 可以有更多方法
}

示例:定义一个图形接口

package mainimport "fmt"// 定义图形接口
type Shape interface {Area() float64Perimeter() float64
}

2. 接口的实现

任何类型只要实现了接口中定义的所有方法,就被认为实现了该接口。

示例:定义矩形和圆形结构体,并实现 Shape 接口

// 矩形结构体
type Rectangle struct {Width  float64Height float64
}// 矩形的面积
func (r Rectangle) Area() float64 {return r.Width * r.Height
}// 矩形的周长
func (r Rectangle) Perimeter() float64 {return 2 * (r.Width + r.Height)
}// 圆形结构体
type Circle struct {Radius float64
}// 圆形的面积
func (c Circle) Area() float64 {return 3.14 * c.Radius * c.Radius
}// 圆形的周长
func (c Circle) Perimeter() float64 {return 2 * 3.14 * c.Radius
}

3. 接口的使用

通过接口类型的变量,可以调用实现该接口的任何类型的方法。

func main() {var s Shapes = Rectangle{Width: 5, Height: 3}fmt.Printf("矩形面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())s = Circle{Radius: 4}fmt.Printf("圆形面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}

4. 完整代码示例

package main  // 声明当前包为main,main包是可执行程序的入口包,编译后会生成可执行文件import "fmt"  // 导入fmt包,用于格式化输入输出操作// 定义图形接口Shape
// 接口在Go中是一种抽象类型,只声明方法签名,不实现方法
// 任何结构体只要实现了接口中所有的方法,就隐式实现了该接口
type Shape interface {Area() float64      // 声明计算面积的方法,返回float64类型Perimeter() float64 // 声明计算周长的方法,返回float64类型
}// 定义矩形结构体Rectangle
// 结构体是Go中的复合数据类型,用于封装相关的数据字段
type Rectangle struct {Width  float64  // 矩形的宽度字段,类型为float64(双精度浮点型)Height float64  // 矩形的高度字段,类型为float64
}// 为Rectangle结构体实现Area()方法(实现Shape接口的Area方法)
// (r Rectangle)是方法的接收者,表示该方法属于Rectangle类型
// 接收者r就像其他语言中的this/self,用于访问结构体的字段
func (r Rectangle) Area() float64 {return r.Width * r.Height  // 矩形面积公式:宽×高,返回计算结果
}// 为Rectangle结构体实现Perimeter()方法(实现Shape接口的Perimeter方法)
func (r Rectangle) Perimeter() float64 {return 2 * (r.Width + r.Height)  // 矩形周长公式:2×(宽+高),返回计算结果
}// 定义圆形结构体Circle
type Circle struct {Radius float64  // 圆形的半径字段,类型为float64
}// 为Circle结构体实现Area()方法(实现Shape接口的Area方法)
func (c Circle) Area() float64 {return 3.14 * c.Radius * c.Radius  // 圆形面积公式:π×半径²(这里用3.14近似π)
}// 为Circle结构体实现Perimeter()方法(实现Shape接口的Perimeter方法)
func (c Circle) Perimeter() float64 {return 2 * 3.14 * c.Radius  // 圆形周长公式:2×π×半径(这里用3.14近似π)
}// main函数是程序的入口点,程序从这里开始执行
func main() {var s Shape  // 声明一个Shape类型的变量s(接口类型变量)// 接口类型变量可以存储任何实现了该接口的结构体实例(多态特性)s = Rectangle{Width: 10, Height: 5}  // 将Rectangle实例赋值给s// 此时s虽然是Shape类型,但实际存储的是Rectangle实例,调用方法时会执行Rectangle的实现fmt.Printf("矩形的面积是:%.2f,周长是:%.2f\n", s.Area(), s.Perimeter())s = Circle{Radius: 7}  // 将Circle实例赋值给s// 同样,s现在存储的是Circle实例,调用方法时会执行Circle的实现fmt.Printf("圆形面积是:%.2f,周长是:%.2f\n", s.Area(), s.Perimeter())
}

执行结果

矩形的面积是:50.00,周长是:30.00
圆形面积是:153.86,周长是:43.96

三、Go语言的面向对象编程

Go语言没有传统面向对象语言(如Java、C++)中的“类”和“继承”概念,但通过结构体(struct)、接口(interface)等特性,实现了封装、组合、多态等面向对象核心思想,形成了独特的面向对象编程范式。

1. 封装

封装的核心是“数据隐藏”与“行为绑定”:将数据(结构体字段)和操作数据的行为(方法)绑定在一起,并通过访问控制限制数据的直接修改,仅允许通过预定义的方法操作数据。

在Go中,访问控制通过标识符首字母大小写实现:

  • 首字母大写的字段/方法是“公开成员”,可被其他包访问;
  • 首字母小写的字段/方法是“私有成员”,仅允许在同一个包内访问(注意:是“包级私有”,而非“结构体级私有”)。

示例:

package mainimport "fmt"// 定义结构体Person,包含私有字段和公开方法
type Person struct {name string // 私有字段(首字母小写):仅main包内可访问age  int    // 私有字段:仅main包内可访问
}// 公开方法(首字母大写):提供对私有字段name的读取能力,可被其他包调用
func (p *Person) GetName() string {return p.name // 方法与结构体在同一包,可直接访问私有字段
}// 公开方法:提供对私有字段name的修改能力
func (p *Person) SetName(newName string) {p.name = newName // 同一包内,可直接修改私有字段
}func main() {// 初始化Person实例:main函数与Person在同一包,可直接访问私有字段进行初始化p := Person{name: "张三", age: 25}// 同一包内,甚至可以直接访问p.name:// 注意:这里能访问是因为main函数与Person在同一包,并非私有字段可随意访问fmt.Println("直接访问私有字段name:", p.name) // 输出:直接访问私有字段name: 张三// 通过公开方法访问(更规范的做法,即使在包内也推荐)fmt.Println("通过GetName()获取:", p.GetName()) // 输出:通过GetName()获取: 张三p.SetName("李四")fmt.Println("修改后通过GetName()获取:", p.GetName()) // 输出:修改后通过GetName()获取: 李四
}

关键说明:

  • 私有字段的“私有”是针对“其他包” 的限制,同一包内的所有代码(包括函数、方法)都可以直接访问私有字段。
  • 示例中main函数能直接访问p.name,是因为main函数与Person结构体在同一个main包内;如果将Person放到另一个包(如model包),main包就无法直接访问name了,必须通过GetName()等公开方法(跨包访问的正确方式)。

2. 组合

Go通过“结构体嵌套”实现组合(而非传统继承),即一个结构体可以包含另一个结构体作为字段,从而复用其字段和方法,避免了继承带来的“类层次臃肿”和“耦合过重”问题。

示例:

package mainimport "fmt"// 地址结构体:封装地址相关数据
type Address struct {City  string // 城市State string // 国家/地区
}// 员工结构体:通过嵌套Address结构体,复用地址相关字段
type Employee struct {Name    string  // 员工姓名Age     int     // 员工年龄Address Address // 嵌套Address结构体,组合其字段
}func main() {// 初始化员工实例,同时初始化嵌套的Addressemp := Employee{Name: "王五",Age:  30,Address: Address{City:  "北京",State: "中国",},}// 访问组合的字段:通过“结构体.嵌套结构体.字段”的方式fmt.Printf("员工 %s 的地址是 %s, %s\n", emp.Name, emp.Address.City,  // 访问嵌套结构体的City字段emp.Address.State) // 访问嵌套结构体的State字段// 输出:员工 王五 的地址是 北京, 中国
}

优势:

  • 组合是“has-a”(有一个)的关系(如“员工有一个地址”),逻辑更清晰;
  • 可以灵活组合多个结构体,无需关心继承层次,降低代码耦合。

3. 多态

Go通过接口实现多态:接口定义了一组方法签名,任何结构体只要“隐式实现”了接口的所有方法,就属于该接口类型。在使用接口变量时,无需关心其实际存储的结构体类型,只需调用接口方法,即可自动执行对应结构体的实现——这就是多态的核心。

示例(基于之前的Shape接口):

package mainimport "fmt"// 定义接口Shape:声明图形的通用行为
type Shape interface {Area() float64      // 计算面积Perimeter() float64 // 计算周长
}// 矩形结构体:实现Shape接口
type Rectangle struct {Width  float64Height float64
}// 实现Shape的Area方法
func (r Rectangle) Area() float64 {return r.Width * r.Height
}// 实现Shape的Perimeter方法
func (r Rectangle) Perimeter() float64 {return 2 * (r.Width + r.Height)
}// 圆形结构体:实现Shape接口
type Circle struct {Radius float64
}// 实现Shape的Area方法
func (c Circle) Area() float64 {return 3.14 * c.Radius * c.Radius
}// 实现Shape的Perimeter方法
func (c Circle) Perimeter() float64 {return 2 * 3.14 * c.Radius
}func main() {var s Shape // 声明接口类型变量ss = Rectangle{Width: 10, Height: 5} // s存储矩形实例(矩形实现了Shape)fmt.Printf("矩形:面积=%.2f, 周长=%.2f\n", s.Area(), s.Perimeter()) // 输出:矩形:面积=50.00, 周长=30.00s = Circle{Radius: 7} // s存储圆形实例(圆形实现了Shape)fmt.Printf("圆形:面积=%.2f, 周长=%.2f\n", s.Area(), s.Perimeter()) // 输出:圆形:面积=153.86, 周长=43.96
}

关键说明:

  • 接口变量s可以存储任何实现了Shape接口的结构体(矩形、圆形等),体现了“接口的通用性”;
  • 调用s.Area()时,Go会自动根据s中实际存储的结构体类型(矩形/圆形),执行对应的Area实现,体现了“同一种行为,不同实现”的多态特性。

四、小结

今天我们学习了Go语言中的结构体、接口以及基于它们实现的面向对象编程方式。

  • 结构体:允许我们创建自定义的数据类型,将不同类型的数据组合在一起,方便管理和操作相关数据。
  • 接口:定义了一组方法的签名,任何实现了这些方法的类型都被认为实现了该接口,为不同类型提供统一调用方式,增强代码的灵活性和扩展性。
  • 面向对象编程:Go的面向对象编程更注重“组合优于继承”“接口隐式实现”,通过结构体封装数据与行为,通过组合复用代码,通过接口实现多态,整体设计简洁灵活,避免了传统面向对象的复杂特性。

五、实战:智能设备管理系统

以下实战案例是一个更贴近实际开发场景的实战案例:智能设备管理系统

以下案例会综合运用结构体接口封装组合多态等知识点,模拟一个管理多种智能设备(如智能灯、恒温器、摄像头)的系统,功能包括设备状态监控、远程控制、数据统计等,更具实用性和扩展性。

实战案例:智能设备管理系统

需求说明

实现一个能管理多种智能设备的系统,支持:

  1. 设备的启动/关闭/状态查询(通用功能);
  2. 每种设备的特有功能(如灯调节亮度、恒温器调节温度);
  3. 统一管理所有设备,批量执行操作并统计状态。
完整代码实现
package mainimport ("fmt""time"
)// -------------- 1. 定义核心接口(多态基础)--------------
// Device 接口:所有智能设备的通用行为契约
type Device interface {ID() string               // 获取设备唯一标识Start() error             // 启动设备Shutdown() error          // 关闭设备GetStatus() string        // 获取设备当前状态DeviceType() string       // 获取设备类型
}// -------------- 2. 基础结构体(封装与组合)--------------
// BaseDevice 基础设备结构体:封装所有设备的共性字段和通用方法
// 私有字段通过公开方法访问(封装),供其他设备组合复用(组合)
type BaseDevice struct {deviceID   string    // 设备唯一ID(私有字段,包内可见)name       string    // 设备名称(私有字段)status     string    // 设备状态(私有字段:"off" / "on")createdAt  time.Time // 设备创建时间(私有字段)
}// 初始化基础设备(构造函数)
func NewBaseDevice(deviceID, name string) BaseDevice {return BaseDevice{deviceID:  deviceID,name:      name,status:    "off", // 初始状态为关闭createdAt: time.Now(),}
}// ID 实现Device接口的ID方法(公开方法,供外部获取设备ID)
func (b *BaseDevice) ID() string {return b.deviceID
}// Start 基础启动逻辑(通用实现,可被组合的设备复用)
func (b *BaseDevice) Start() error {if b.status == "on" {return fmt.Errorf("设备已启动")}b.status = "on"return nil
}// Shutdown 基础关闭逻辑(通用实现)
func (b *BaseDevice) Shutdown() error {if b.status == "off" {return fmt.Errorf("设备已关闭")}b.status = "off"return nil
}// GetStatus 实现Device接口的状态查询(通用实现)
func (b *BaseDevice) GetStatus() string {return fmt.Sprintf("%s(%s)", b.name, b.status)
}// -------------- 3. 具体设备实现(组合与多态)--------------// 智能灯(继承基础设备的功能,添加特有功能)
type SmartLight struct {BaseDevice       // 组合基础设备(复用ID/Start/Shutdown等功能)Brightness int   // 亮度(0-100,特有字段)Color      string // 灯光颜色(特有字段)
}// NewSmartLight 智能灯构造函数
func NewSmartLight(deviceID, name string) *SmartLight {return &SmartLight{BaseDevice: NewBaseDevice(deviceID, name),Brightness: 50, // 默认亮度50%Color:      "white",}
}// DeviceType 实现Device接口,返回设备类型(特有实现)
func (s *SmartLight) DeviceType() string {return "智能灯"
}// AdjustBrightness 智能灯特有方法:调节亮度
func (s *SmartLight) AdjustBrightness(level int) error {if s.BaseDevice.status != "on" {return fmt.Errorf("设备未启动,无法调节亮度")}if level < 0 || level > 100 {return fmt.Errorf("亮度值必须在0-100之间")}s.Brightness = levelreturn nil
}// 恒温器(另一种设备,同样组合基础设备)
type Thermostat struct {BaseDevice     // 组合基础设备TargetTemp float64 // 目标温度(特有字段)CurrentTemp float64 // 当前温度(特有字段)
}// NewThermostat 恒温器构造函数
func NewThermostat(deviceID, name string) *Thermostat {return &Thermostat{BaseDevice: NewBaseDevice(deviceID, name),TargetTemp: 25.0, // 默认目标温度25℃CurrentTemp: 23.5,}
}// DeviceType 实现Device接口,返回设备类型
func (t *Thermostat) DeviceType() string {return "恒温器"
}// SetTargetTemp 恒温器特有方法:设置目标温度
func (t *Thermostat) SetTargetTemp(temp float64) error {if t.BaseDevice.status != "on" {return fmt.Errorf("设备未启动,无法设置温度")}if temp < 16 || temp > 30 {return fmt.Errorf("温度范围必须在16-30℃之间")}t.TargetTemp = tempreturn nil
}// 摄像头(第三种设备)
type Camera struct {BaseDevice   // 组合基础设备Resolution string // 分辨率(特有字段)IsRecording bool  // 是否正在录像(特有字段)
}// NewCamera 摄像头构造函数
func NewCamera(deviceID, name string) *Camera {return &Camera{BaseDevice: NewBaseDevice(deviceID, name),Resolution: "1080p",IsRecording: false,}
}// DeviceType 实现Device接口,返回设备类型
func (c *Camera) DeviceType() string {return "摄像头"
}// StartRecording 摄像头特有方法:开始录像
func (c *Camera) StartRecording() error {if c.BaseDevice.status != "on" {return fmt.Errorf("设备未启动,无法录像")}if c.IsRecording {return fmt.Errorf("已在录像中")}c.IsRecording = truereturn nil
}// -------------- 4. 设备管理器(统一管理与多态应用)--------------
// DeviceManager 设备管理器:统一管理所有设备
type DeviceManager struct {devices map[string]Device // 用接口类型存储所有设备(多态关键)
}// NewDeviceManager 初始化设备管理器
func NewDeviceManager() *DeviceManager {return &DeviceManager{devices: make(map[string]Device),}
}// AddDevice 添加设备到管理器
func (m *DeviceManager) AddDevice(d Device) {m.devices[d.ID()] = d
}// StartAll 批量启动所有设备
func (m *DeviceManager) StartAll() {fmt.Println("\n===== 批量启动所有设备 =====")for id, d := range m.devices {err := d.Start()if err != nil {fmt.Printf("设备[%s]启动失败:%v\n", id, err)} else {fmt.Printf("设备[%s]启动成功:%s\n", id, d.GetStatus())}}
}// ShutdownAll 批量关闭所有设备
func (m *DeviceManager) ShutdownAll() {fmt.Println("\n===== 批量关闭所有设备 =====")for id, d := range m.devices {err := d.Shutdown()if err != nil {fmt.Printf("设备[%s]关闭失败:%v\n", id, err)} else {fmt.Printf("设备[%s]关闭成功:%s\n", id, d.GetStatus())}}
}// ShowStatus 展示所有设备状态
func (m *DeviceManager) ShowStatus() {fmt.Println("\n===== 所有设备状态 =====")for _, d := range m.devices {fmt.Printf("[%s] %s:%s\n", d.ID(), d.DeviceType(), d.GetStatus())}
}// -------------- 5. 主函数(演示流程)--------------
func main() {// 1. 创建设备管理器manager := NewDeviceManager()// 2. 创建各种设备(结构体实例化)light := NewSmartLight("light_001", "客厅灯")thermostat := NewThermostat("thermo_001", "卧室恒温器")camera := NewCamera("cam_001", "门口摄像头")// 3. 将设备添加到管理器(接口类型存储,多态)manager.AddDevice(light)manager.AddDevice(thermostat)manager.AddDevice(camera)// 4. 展示初始状态(所有设备默认关闭)manager.ShowStatus()// 5. 批量启动所有设备manager.StartAll()// 6. 调用各设备的特有功能(体现封装的字段访问控制)fmt.Println("\n===== 设备特有功能操作 =====")// 智能灯调节亮度if err := light.AdjustBrightness(80); err != nil {fmt.Println("调节亮度失败:", err)} else {fmt.Printf("客厅灯亮度已调节至%d%%\n", light.Brightness)}// 恒温器设置目标温度if err := thermostat.SetTargetTemp(26.5); err != nil {fmt.Println("设置温度失败:", err)} else {fmt.Printf("卧室恒温器目标温度已设置为%.1f℃\n", thermostat.TargetTemp)}// 摄像头开始录像if err := camera.StartRecording(); err != nil {fmt.Println("录像启动失败:", err)} else {fmt.Println("门口摄像头已开始录像")}// 7. 批量关闭所有设备manager.ShutdownAll()
}
代码执行结果
===== 所有设备状态 =====
[light_001] 智能灯:客厅灯(off)
[thermo_001] 恒温器:卧室恒温器(off)
[cam_001] 摄像头:门口摄像头(off)===== 批量启动所有设备 =====
设备[light_001]启动成功:客厅灯(on)
设备[thermo_001]启动成功:卧室恒温器(on)
设备[cam_001]启动成功:门口摄像头(on)===== 设备特有功能操作 =====
客厅灯亮度已调节至80%
卧室恒温器目标温度已设置为26.5℃
门口摄像头已开始录像===== 批量关闭所有设备 =====
设备[light_001]关闭成功:客厅灯(off)
设备[thermo_001]关闭成功:卧室恒温器(off)
设备[cam_001]关闭成功:门口摄像头(off)

案例知识点解析

这个案例完整覆盖了结构体、接口、面向对象的核心知识点,具体对应如下:

  1. 结构体(自定义数据类型)

    • 定义了BaseDevice(基础设备)、SmartLight(智能灯)等结构体,封装了设备的属性(如deviceIDstatusBrightness),实现了数据的结构化管理。
    • 通过构造函数(如NewSmartLight)规范结构体实例化过程。
  2. 接口(行为契约与多态)

    • Device接口定义了所有设备的通用行为(Start()/Shutdown()等),任何设备只要实现了这些方法,就属于Device类型。
    • 设备管理器DeviceManager通过map[string]Device存储设备,利用接口的多态特性,统一管理不同类型的设备(无需关心具体是灯还是摄像头)。
  3. 封装(数据隐藏与访问控制)

    • BaseDevice中的字段(如deviceIDstatus)首字母小写,为包内私有,外部无法直接修改,只能通过公开方法(如Start()ID())操作,保证数据安全性。
    • 例如:设备状态status只能通过Start()/Shutdown()方法修改,避免了直接赋值导致的状态混乱。
  4. 组合(代码复用)

    • 所有具体设备(SmartLight/Thermostat/Camera)都嵌套了BaseDevice,复用了基础功能(如设备ID管理、启动/关闭逻辑),避免重复代码。
    • 组合是“has-a”关系(如“智能灯有一个基础设备的属性”),比传统继承更灵活,设备可自由组合多个基础功能(如未来可添加“网络模块”结构体实现联网功能)。
  5. 多态(同一接口,不同实现)

    • 设备管理器调用d.Start()时,会根据d实际存储的设备类型(灯/恒温器/摄像头)执行对应实现(虽然BaseDevice提供了默认Start(),但未来可在具体设备中重写以实现特殊逻辑)。
    • 新增设备(如智能窗帘)时,只需实现Device接口,无需修改管理器代码,符合“开闭原则”,扩展性极强。

这个案例更贴近实际开发中的“设备管理”“插件系统”等场景,通过接口抽象通用行为,通过组合复用代码,通过封装保证数据安全,充分体现了Go语言面向对象编程的简洁与灵活。

通过这个实战案例,希望能帮助大家更好地理解和运用今天所学的知识。不断实践,你将在Go语言的学习中取得更大的进步!
在这里插入图片描述

专栏预告:下一篇深入Go语言学习并发编程与错误处理,我们将探索Go语言强大的并发编程能力以及优雅的错误处理机制,这将使你能够开发出高性能、健壮的Go应用程序,敬请期待! 😊

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

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

相关文章

Python入门第六课:现代开发与前沿技术

异步编程(asyncio) 1. 协程基础 import asyncio import time# 定义协程函数 async def say_after(delay, message):await asyncio.sleep(delay)print(message)# 主协程 async def main():print(f"开始时间: {time.strftime(%X)}")# 顺序执行await say_after(2, 你…

STM32移植LVGL9.2.1教程

一、环境说明 &#xff08;1&#xff09;开发板&#xff1a;STM32F401RCT6核心板&#xff08;网上很多&#xff0c;价格只有几块钱&#xff09; &#xff08;2&#xff09;屏幕&#xff1a;2.8寸spi屏gt911触摸 转接板&#xff08;某宝有卖&#xff0c;没有推广自行搜索&…

python学智能算法(二十九)|SVM-拉格朗日函数求解中-KKT条件理解

【1】引言 前序学习阶段中&#xff0c;我们掌握了最佳分割超平面对应的构造拉格朗日函数极值为&#xff1a; L(w,b,α)∑i1mαi−12∑i,j1mαiαjyiyjxiTxjL(w,b,\alpha)\sum_{i1}^{m}\alpha_{i}-\frac{1}{2}\sum_{i,j1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}x_{i}^{T}x_{j}L(w,…

大模型应用开发1-认识大模型

1.基础概念 1.1 AI的概念&#xff1a; AI&#xff0c;⼈⼯智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;使机器能够像⼈类⼀样思考、学习和解决问题的技术。AI发展⾄今⼤概可以分为三个阶段&#xff1a;其中&#xff0c;深度学习领域的自然语言处理&#…

Linux 远程连接解析:SSH 协议理论与应用

Linux 远程连接解析&#xff1a;SSH 协议理论与应用在网络互联的时代&#xff0c;远程管理服务器已成为常态。SSH&#xff08;Secure Shell&#xff09;作为一种安全的网络协议&#xff0c;凭借其加密机制和灵活的功能&#xff0c;成为 Linux 系统远程操作的事实标准。本文将从…

ubuntu22.04系统入门 linux入门 简单命令基础复习 实现以及实践

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 兑换码要是过期了&#xff0c;可以私信我获取最新兑换码&#xff01;&a…

软考中级-信息安全工程师-每日一学(1)

前提概要本文章主要用于分享软考中级-信息安全工程师-学习&#xff0c;以下是一些个人理解&#xff0c;请大家结合参考其他文章中的相关信息及个人经验进行归纳和补充&#xff0c;内容会存在一定错误&#xff0c;希望读者多多评论批评&#xff0c;本人在此先说感谢啦。1.密码学…

EEG手工特征提取总结

目录一、引言EEG信号简介EEG特征提取的重要性本次汇报目的与内容概述二、EEG信号核心特征时域特征 (Time-Domain Features)频域特征 (Frequency-Domain Features)三、EEG信号高级特征时频域特征 (Time-Frequency Domain Features)空间域特征 (Spatial-Domain Features)复杂动力…

React 路由守卫

下面&#xff0c;我们来系统的梳理关于 React Router 路由守卫 的基本知识点&#xff1a;一、路由守卫概述 1.1 什么是路由守卫 路由守卫是一种在用户导航到特定路由之前或离开特定路由时执行逻辑的机制。它允许开发者控制用户访问权限、验证条件或执行数据预加载等操作。 1.2 …

7月31日作业

1&#xff1a;请使用函数模板&#xff0c;写一个能够针对所有数据类型的数据的快速排序函数 并多写几个数组做测试代码#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector…

客户服务自动化:如何用CRM减少50%人工工单?

通过CRM系统实现客户服务自动化&#xff0c;企业可以显著减少人工工单的数量&#xff0c;提升整体服务效率。那么如何利用CRM系统实现客户服务自动化&#xff1f;帮助企业从根本上解决人工工单处理的难题&#xff0c;提升服务质量&#xff0c;优化资源配置&#xff0c;最终实现…

常用设计模式系列(十四)—模板方法模式

常用设计模式系列&#xff08;十四&#xff09;—模板方法模式 第一节 前言 之前我完成了创建型设计模式和结构型设计模式&#xff0c;我们今天将踏入设计模式的第三章&#xff1a;行为型设计模式&#xff0c;你是否还记得什么是行为型设计模式吗&#xff1f;行为型模式&#x…

DoRA详解:从LoRA到权重分解的进化

DoRA​​是一种用于​​大语言模型&#xff08;LLM&#xff09;微调​​的技术&#xff0c;全称为 ​​"Weight-Decomposed Low-Rank Adaptation"​​&#xff08;权重分解的低秩自适应&#xff09;。它是对现有微调方法&#xff08;如 ​​LoRA​​&#xff09;的改…

RocksDB关键设计详解

0 说明 近日工作中使用了 RocksDB。RocksDB 的优点此处无需多说&#xff0c;它的一个 feature 是其有很多优化选项用于对 RocksDB 进行调优。欲熟悉这些参数&#xff0c;必须对其背后的原理有所了解&#xff0c;本文主要整理一些 RocksDB 的 wiki 文档&#xff0c;以备自己参考…

Kotlin -> 普通Lambda vs 挂起Lambda

1. 普通Lambda vs 挂起Lambda的本质区别 1.1 普通Lambda&#xff08;同步执行&#xff09; val lambda: (Int) -> String { it.toString() }// 编译器生成&#xff1a; class Lambda$1 : Function1<Int, String> {override fun invoke(p1: Int): String {return p1.t…

Apache Ignite 中如何配置和启用各类监控指标

这段文档是关于 Apache Ignite 中如何配置和启用各类监控指标&#xff08;Metrics&#xff09; 的详细说明。核心思想是&#xff1a;“指标收集有性能开销&#xff0c;因此默认不开启所有指标&#xff0c;需要你按需手动开启。” 下面我们来逐层拆解、通俗易懂地理解这些内容。…

uniapp x swiper/image组件mode=“aspectFit“ 图片有的闪现后黑屏

部分安卓机针对大写.JPG 有的竖图正常&#xff0c;横图/正方形不对。解决方案&#xff1a;加border-radius: 1rpx;就行<!-- 图片预览弹出框 --><fui-backdrop v-model:visible"imgPreviewVisible" :closable"true" onclick"imgPreviewVisibl…

conda安装jupter

conda自带的jupter本来在base里没有在pytorch环境中 安装jupter conda install nb_conda 此扩展程序在 Jupyter 文件浏览器中添加了一个 Conda 选项卡。选择 Conda 选项卡将显示&#xff1a; 当前存在的 Conda 环境列表当前配置的通道中可用的 Conda 包列表&#xff08;htt…

嵌入式操作系统快速入门(1):快速入门操作系统常见基础概念

快速体会操作系统常见基础概念 1 初识基本概念 1.1 操作系统 一个软件程序&#xff1b;用于解决计算机多任务执行时的资源争抢问题&#xff1b;管理计算机中的各种资源&#xff0c;确保计算机正常完成各种工作&#xff08;任务&#xff09;&#xff0c;解决多任务环境中任务的调…

网络安全-同形异义字攻击:眼见并非为实(附案例详解)

什么是同形异义字攻击&#xff1f;对人眼而言&#xff0c;一切看起来完全正常。但实际上&#xff0c;例如单词 Ηоmоgraph 并不完全等同于单词 Homograph。它们之间的差异非常细微&#xff0c;难以察觉。Ηоmоgraph 实际上包含了几个非拉丁字母。在本例中&#xff0c;我们将…