有基础转Go语言学习笔记(3. 面向对象篇)

有基础转Go语言学习笔记(3. 面向对象篇)

封装

在Go语言中,封装是通过使用结构体(structs)和方法(methods)来实现的。Go语言的封装不同于传统面向对象编程语言中的类(class)系统,但它提供了类似的功能。封装在Go中主要体现在控制对结构体字段的访问权限上。

封装的实现

1. 定义结构体

结构体是组合相关属性的方式。你可以将数据封装在一个结构体内。

type person struct {name stringage  int
}

在这个例子中,person结构体封装了 nameage字段。

2. 使用导出和非导出字段
  • 导出字段(Public):字段名以大写字母开头的字段是可导出的,意味着它们可以在包外部被访问。
  • 非导出字段(Private):字段名以小写字母开头的字段是不可导出的,它们只能在定义它们的包内部被访问。
type Person struct {Name string  // 导出字段age  int     // 非导出字段
}

在这个例子中,Name是一个导出字段,可以被包外的代码访问,而 age是非导出的,只能在定义它的包内部访问。

3. 使用方法进行操作

通过定义方法(函数与特定类型关联),你可以控制结构体的行为,以及如何访问和修改其内部状态。

func (p *Person) SetAge(age int) {p.age = age
}func (p *Person) Age() int {return p.age
}

在这个例子中,SetAgeAge方法允许外部代码以受控的方式访问和修改私有字段 age

封装的好处

  1. 数据隐藏:封装强制隐藏内部实现的细节,只暴露必要的接口。这有助于减少软件系统的复杂性。
  2. 控制访问:提供了一种控制对对象状态的访问和修改的机制。
  3. 易于维护:通过封装实现的代码更易于修改和维护。

完整示例

package mainimport ("fmt"
)// Person 结构体,封装了个人的详细信息
type Person struct {Name string // 公开字段age  int    // 私有字段
}// NewPerson 是一个构造函数,返回一个新的Person实例
func NewPerson(name string, age int) *Person {return &Person{Name: name,age:  age,}
}// SetAge 方法设置Person的年龄
func (this *Person) SetAge(age int) {if age > 0 {this.age = age}
}// GetAge 方法返回Person的年龄
func (this *Person) GetAge() int {return this.age
}func main() {// 创建一个Person实例person := NewPerson("Alice", 30)// 访问并打印Person的公开字段fmt.Println("Name:", person.Name)// 访问私有字段通过方法fmt.Println("Age:", person.GetAge())// 设置新的年龄person.SetAge(31)fmt.Println("New Age:", person.GetAge())
}

输出:

Name: Alice
Age: 30
New Age: 31

接收器(Receiver)

在Go语言中,接收器(receiver)是定义在方法中的一个特殊参数,它指定了该方法绑定到哪种类型上。这允许你在特定类型的实例上调用该方法,类似于其他面向对象语言中的方法。在Go中,接收器可以是该类型的值或该类型的指针。

值接收器

值接收器使用类型的值来调用方法。当使用值接收器时,方法在调用时会接收调用对象的一个副本。

type Person struct {Name stringAge  int
}func (p Person) SayHello() {fmt.Println("Hello, my name is", p.Name)
}

在这个例子中,SayHello方法通过值接收器绑定到 Person类型上。当你调用这个方法时,pPerson实例的一个副本。

指针接收器

指针接收器使用指向类型的指针来调用方法。这意味着方法可以修改接收器指向的值。

func (p *Person) SetAge(newAge int) {p.Age = newAge
}

在这里,SetAge方法使用指针接收器,所以它可以修改 Person实例的 Age字段。

选择值接收器还是指针接收器

选择使用值接收器还是指针接收器主要取决于以下几点:

  1. 修改对象状态:如果你需要在方法中修改对象的状态,应该使用指针接收器。
  2. 效率和性能:如果对象比较大,使用指针接收器效率更高,因为它避免了复制整个对象。
  3. 一致性:为了保持一致性,如果类型的某些方法需要指针接收器,则最好让该类型的所有方法都使用指针接收器。
  4. 值类型的不变性:使用值接收器可以保持值类型的不变性,因为它总是在方法调用时创建值的副本。
接收器在方法中的作用

接收器允许你将函数与特定类型的实例关联,类似于面向对象编程中的方法。这样,你可以对这个类型的每个实例调用它的方法,就像是这个类型自己的行为一样。接收器是Go语言实现面向对象编程风格的关键元素之一。

继承

虽然Go语言没有像Python或C++中那样的类继承概念,但它使用结构体嵌入和组合来实现类似继承的功能。对于熟悉Python和C++的开发者来说,理解Go的这种继承方式需要转换一下思维方式。

结构体嵌入和组合

在Go中,继承是通过在一个结构体中嵌入另一个结构体来实现的。这种方法被称为组合(Composition)。嵌入的结构体的方法和字段可以被外层结构体直接访问,就好像它们是外层结构体的一部分。

示例

假设我们有一个基本的 Animal结构体,然后我们想要创建一个 Dog结构体,它拥有 Animal的所有特性,并添加一些自己的特性。

type Animal struct {Name string
}func (a *Animal) Speak() {fmt.Println(a.Name, "makes a sound")
}type Dog struct {Animal // 嵌入AnimalBreed  string
}func (d *Dog) Speak() {fmt.Println(d.Name, "barks")
}

在这个例子中,Dog嵌入了 Animal。这意味着 Dog自动获得了 Animal的所有字段和方法。我们还可以为 Dog添加新的方法或重写(Override)继承的方法,就像上面的 Speak方法那样。

调用方法

当调用一个嵌入结构体的方法时,如果外层结构体没有同名方法,则会调用嵌入结构体的方法。

func main() {d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Shepherd"}d.Speak() // 输出: Rex barksd.Animal.Speak() // 输出: Rex makes a sound
}

在这里,调用 d.Speak()时,由于 Dog结构体有自己的 Speak方法,所以调用的是 Dog的方法。而 d.Animal.Speak()则显式地调用了 AnimalSpeak方法。

对比Python和C++的继承

  • Python/C++:这些语言使用类继承,支持基类和派生类之间的直接关系,允许使用 super函数调用基类的方法。
  • Go:Go使用结构体组合而非继承。虽然没有直接的基类-派生类关系,但通过嵌入结构体,可以实现类似的功能。Go中没有 super关键字,但可以通过直接调用嵌入结构体的方法来实现类似的行为。

接口(Interface)和多态(Polymorphic)

在Go语言中,接口和多态是实现灵活、可扩展代码的核心概念。Go的接口提供了一种声明行为的方式,而多态则允许你以统一的方式使用实现了相同接口的不同类型。

接口(Interface)

接口在Go中是一组方法签名的集合。当一个类型提供了接口中所有方法的实现,它就被认为实现了该接口。这种实现是隐式的,不需要像Java或C#中那样显式声明。

定义接口
type Shape interface {Area() float64Perimeter() float64
}

这个 Shape接口定义了两个方法:AreaPerimeter。任何提供了这两个方法的类型都隐式地实现了 Shape接口。

实现接口
type Rectangle struct {Width, Height float64
}func (r Rectangle) Area() float64 {return r.Width * r.Height
}func (r Rectangle) Perimeter() float64 {return 2 * (r.Width + r.Height)
}

在这里,Rectangle类型提供了 Shape接口要求的 AreaPerimeter方法,因此它实现了 Shape接口。

多态(Polymorphic)

多态是面向对象编程中的一个核心概念,它允许你以统一的方式处理不同类型的实例。在Go中,多态是通过接口实现的。

使用多态

你可以编写接受接口类型参数的函数或方法,然后用实现了该接口的任何类型的实例调用它。

func PrintShapeInfo(s Shape) {fmt.Println("Area:", s.Area())fmt.Println("Perimeter:", s.Perimeter())
}func main() {r := Rectangle{Width: 10, Height: 5}PrintShapeInfo(r)
}

PrintShapeInfo函数中,参数 sShape接口类型。这意味着你可以传递任何实现了 Shape接口的类型(如 Rectangle)的实例。

接口的好处

  1. 解耦:接口提供了一种方式来解耦函数和方法的实现,从而使代码更灵活、更易于扩展和维护。
  2. 测试:接口使得单元测试变得更加容易,因为你可以使用模拟对象(mocks)来实现接口。
  3. 多态性:接口的多态性允许你编写更通用的函数和方法,可以处理不同类型的对象,只要它们实现了相同的接口。

完整示例

下面是一个完整的Go程序示例,展示了之前讨论的接口和多态概念。这个程序定义了一个 Animal接口,以及几种实现了这个接口的动物类型。程序中还包含了一个演示多态性的函数和 main函数,用于运行程序。

package main  import (  "fmt"  
)  // Animal 接口定义了一个方法 Speak  
type Animal interface {  Speak() string  
}  // Dog 类型实现了 Animal 接口  
type Dog struct{}  func (d *Dog) Speak() string {  return "Woof!"  
}  // Cat 类型实现了 Animal 接口  
type Cat struct{}  func (c *Cat) Speak() string {  return "Meow!"  
}  // Cow 类型实现了 Animal 接口  
type Cow struct{}  func (cow *Cow) Speak() string {  return "Moo!"  
}  // AnimalSpeak 接收 Animal 接口类型的参数  
// 并调用其 Speak 方法  
func AnimalSpeak(a Animal) {  fmt.Println(a.Speak())  
}  // main 函数是程序的入口  
func main() {  animals := []Animal{&Dog{}, &Cat{}, &Cow{}}  for _, animal := range animals {  AnimalSpeak(animal)  }  
}

在这个程序中:

  • 我们定义了一个 Animal接口,该接口有一个方法 Speak
  • DogCatCow类型分别实现了 Animal接口。
  • AnimalSpeak函数接受任何实现了 Animal接口的类型,并调用其 Speak方法。
  • main函数中,我们创建了一个包含不同动物的切片,并对每个动物调用了 AnimalSpeak函数。

空接口

在Go语言中,空接口(interface{})是一个特殊的类型,它不包含任何方法声明。由于Go中的接口是隐式实现的,任何类型都至少实现了零个方法,因此可以说所有类型都实现了空接口。这使得空接口在Go中具有独特的灵活性和用途。

相当于 Java中的 Object

空接口的类型

空接口表示为 interface{},它可以包含任何类型的值:

  • 基本类型:整型、浮点型、布尔型、字符串等。
  • 复合类型:数组、结构体、切片、映射等。
  • 函数类型:可以包含任意函数。
  • 通道、指针、接口类型:也可以赋值给空接口。

由于空接口没有定义任何方法,因此任何类型的值都可以被赋给空接口变量。

空接口的使用

1. 作为通用容器

由于空接口可以包含任何类型,因此它常被用作存储不确定类型值的通用容器。

var anything interface{}
anything = "Hello, World"
anything = 123
anything = []int{1, 2, 3}
2. 函数参数和返回值

空接口允许函数接受任意类型的参数或返回任意类型的数据。

func PrintValue(v interface{}) {fmt.Println(v)
}
3. 类型断言和类型判断

你可以使用类型断言来提取空接口中的实际类型。

// 判断any是否为string
if str, ok := anything.(string); ok {fmt.Println(str)
}
情况 1: 断言成功

anything变量实际上是一个 string类型时:

  • str, ok := anything.(string)会成功执行。
  • str将获得 anything变量的值,因为 anything确实是一个 string类型。
  • ok变量将被设置为 true,表示类型断言成功。

例如,如果 anything := "hello",那么在执行类型断言后,str将是 "hello"ok将是 true

情况 2: 断言失败

anything变量不是一个 string类型时:

  • str, ok := anything.(string)不会导致程序崩溃,而是安全地返回两个值。
  • str将被赋予 string类型的零值,即空字符串 ""。这是因为类型断言失败,str无法获得 anything的值。
  • ok变量将被设置为 false,表示类型断言没有成功。

例如,如果 anything := 42(一个 int类型),那么在执行类型断言后,str将是 ""(空字符串),ok将是 false

4. 在数据结构中使用

在诸如切片、映射等数据结构中,空接口可以用来存储各种不同类型的数据。

var mixedSlice []interface{}
mixedSlice = append(mixedSlice, 42, "foo", true)

空接口的局限性

  • 类型安全:使用空接口牺牲了类型安全。在使用空接口存储值时,你需要额外小心,特别是在类型断言和类型转换时。
  • 性能考虑:频繁地进行类型断言或反射可能会影响程序的性能。

反射机制

Go语言中的反射是一个强大且复杂的功能,它允许程序在运行时检查、修改变量的类型和值,以及调用其关联的方法。反射主要由 reflect包提供支持。理解反射的关键在于理解Go中的类型(Type)和值(Value)的概念。

基本概念

在Go的 reflect包中,主要有两个重要的类型:reflect.Typereflect.Value

  • reflect.Type:提供了关于Go值的类型信息,比如其名称、种类(如是结构体、整型、切片等)。
  • reflect.Value:包含了一个具体的Go值,以及与之相关的方法来操作这个值(比如获取或设置值)。

使用反射

获取类型信息(reflect.Type)
var x float64 = 3.4
t := reflect.TypeOf(x)
fmt.Println("Type:", t.Name()) // 输出: Type: float64

这里,reflect.TypeOf函数用于获取变量 x的类型信息。

获取值信息(reflect.Value)
v := reflect.ValueOf(x)
fmt.Println("Value:", v.Float()) // 输出: Value: 3.4

reflect.ValueOf函数返回了一个 reflect.Value类型的值,代表了变量 x的值。

修改值

要修改一个值,你需要确保这个值是可设置的(Settable)。通常这意味着你需要传递一个变量的指针给 reflect.ValueOf

var y float64 = 3.4
p := reflect.ValueOf(&y) // 注意:这里传递的是指针
v := p.Elem()
v.SetFloat(7.1)
fmt.Println(y) // 输出: 7.1

这里,p.Elem()提供了一个指针指向的值的 reflect.Value,这个值是可以设置的。

反射的使用场景

  1. 动态调用方法:反射可以在运行时动态调用对象的方法,即使这些方法在编译时并不确定。
  2. 检查类型:在需要处理多种类型,且类型在编译时不可知的情况下,反射是处理这些值的有效方式。
  3. 实现通用功能:反射常用于实现那些需要操作各种类型的通用函数,如格式化输出、序列化和反序列化等。

反射的局限性

  • 性能开销:反射操作比正常的直接调用有更多的运行时开销。
  • 复杂性和可读性:反射代码通常更复杂,可读性较差。
  • 类型安全:反射牺牲了部分类型安全性,因为许多检查都是在运行时进行的。

反射遍历复杂对象

在Go语言中,使用反射深度遍历复杂类型(如结构体中嵌套的结构体或数组)可以是一个复杂的任务。但是,通过 reflect包的功能,我们可以递归地访问和操作这些复杂类型的每个字段。以下是一个示例,展示了如何使用反射来深度遍历一个结构体,包括它的嵌套字段:

示例代码
package mainimport ("fmt""reflect"
)// 遍历结构体的函数
func walk(v reflect.Value, depth int) {// 获取v的类型typ := v.Type()switch v.Kind() {case reflect.Struct:for i := 0; i < v.NumField(); i++ {field := v.Field(i)fieldType := typ.Field(i)fmt.Printf("%*s%s (%s): ", depth*2, "", fieldType.Name, field.Type())if field.CanInterface() {fmt.Println(field.Interface())} else {fmt.Println("unexported field")}// 递归调用walkwalk(field, depth+1)}case reflect.Slice, reflect.Array:for i := 0; i < v.Len(); i++ {fmt.Printf("%*s%d: ", depth*2, "", i)walk(v.Index(i), depth+1)}}
}type Person struct {Name    stringAge     intFriends []stringParent  *Person
}func main() {james := Person{Name:    "James",Age:     35,Friends: []string{"David", "Emma"},Parent:  &Person{Name: "John"},}walk(reflect.ValueOf(james), 0)
}
解释
  • 这个程序定义了一个名为 walk的函数,它使用递归来遍历结构体的每个字段。
  • 这个函数检查传入值的类型,并根据类型进行不同的处理。如果是结构体,它遍历每个字段;如果是切片或数组,它遍历每个元素。
  • 我们使用 reflect.Valuereflect.Type来获取关于值和类型的信息,并通过 Field方法访问结构体的字段。
  • 对于结构体中的每个字段,我们打印字段名、类型和值。
  • 如果字段是可导出的(即公有的),可以通过 CanInterfaceInterface方法获取其值。
  • depth参数用于在打印时生成缩进,使输出更容易阅读。
注意
  • 反射中的 CanInterface方法用于检查是否可以安全地调用 Interface方法。对于非导出(私有)字段,CanInterface会返回 false,此时调用 Interface会引发panic。
  • 递归遍历复杂类型时,特别要注意循环引用的情况,这可能导致无限递归。在实际应用中,你可能需要添加一些逻辑来处理这种情况。

反射遍历指针对象

package main  import (  "fmt"  "reflect")  type User struct {  Id   int  Name string  Age  int  
}  func (this *User) Call() {  fmt.Println("user is called ...")  fmt.Printf("%v\n\n", this)  
}  func main() {  user := User{1, "Chance", 18}  DoFiledAndMethod(user)  fmt.Println("--------------------------------------------------")  DoFiledAndMethodOnPtr(&user)  
}  func DoFiledAndMethod(obj interface{}) {  // 获取obj的type  Type := reflect.TypeOf(obj)  fmt.Println("inputType is ", Type.Name())  // 获取obj的value  Value := reflect.ValueOf(obj)  fmt.Println("inputValue is ", Value)  //获取type里面的字段  for i := 0; i < Type.NumField(); i++ {  field := Type.Field(i)  value := Value.Field(i).Interface()  fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)  }  //通过type里面的方法  for i := 0; i < Type.NumMethod(); i++ {  method := Type.Method(i)  fmt.Printf("%s: %v\n", method.Name, method.Type)  }  
}  func DoFiledAndMethodOnPtr(input interface{}) {  Type := reflect.TypeOf(input)  Value := reflect.ValueOf(input)  // If pointer get the underlying element≤  if Type.Kind() == reflect.Ptr {  Type = Type.Elem()  Value = Value.Elem()  }  fmt.Println("inputType is ", Type.Name())  fmt.Println("inputValue is ", Value)  for i := 0; i < Type.NumField(); i++ {  field := Type.Field(i)  value := Value.Field(i).Interface()  fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)  }  for i := 0; i < Type.NumMethod(); i++ {  method := Type.Method(i)  fmt.Printf("%s: %v\n", method.Name, method.Type)  }  
}

输出:

inputType is  User
inputValue is  {1 Chance 18}
Id: int = 1
Name: string = Chance
Age: int = 18
Call: func(main.User)
--------------------------------------------------
inputType is  User
inputValue is  {1 Chance 18}
Id: int = 1
Name: string = Chance
Age: int = 18
Call: func(main.User)

结构体标签

在Go语言中,结构体字段可以通过标签(Tag)附加元数据。这些标签不会直接影响程序的行为,但可以通过反射在运行时读取,常用于序列化/反序列化、表单验证、ORM映射等场景。

结构体标签的基本使用

结构体标签是定义在结构体字段后的字符串字面量,通常由一系列键值对组成,键和值之间使用冒号分隔,多个键值对之间用空格分隔。

type User struct {Name  string `json:"name"`Email string `json:"email"`Age   int    `json:"age" validate:"min=18"`
}

在这个例子中,User结构体的每个字段都有一个 json标签,用于定义JSON序列化时该字段的名称。Age字段还有一个额外的 validate标签,可能被用于字段验证。

读取标签

要读取标签,你需要使用 reflect包。以下是一个示例,演示如何读取结构体字段的标签:

func PrintTags(u User) {t := reflect.TypeOf(u)for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Printf("Field: %s, Tag: '%s'\n", field.Name, field.Tag)}
}func main() {user := User{Name: "Alice", Email: "alice@example.com", Age: 30}PrintTags(user)
}

这段代码使用反射来遍历 User类型的字段,并打印出每个字段的名称和标签。

标签的应用场景
  1. JSON序列化/反序列化:通过 encoding/json包,可以控制结构体如何被转换为JSON,或从JSON转换回来。
  2. 数据库ORM映射:在使用ORM(对象关系映射)框架时,标签可用于定义数据库表的列名、类型等信息。
  3. 表单验证:标签可以用于定义字段的验证规则,例如,必填、最大长度、格式等。
注意事项
  • 结构体标签提供了元数据,但它们本身并不直接改变程序的行为。它们的作用通常体现在与特定库或框架配合时。
  • 不同的库或框架可能对标签的格式和解析方式有不同的要求。例如,jsonxmlgorm等库都有自己的标签格式。
  • 在使用标签时,应遵循库或框架的文档指南,以确保正确地应用标签。

结构体标签在Go中是一种强大的机制,用于提供关于结构体字段的额外信息,尤其在数据序列化和ORM映射等方面非常有用。

json实例

package main  import (  "encoding/json"  "fmt")  type Movie struct {  Title  string   `json:"title"` // 通过指定tag实现json序列化该字段时的key  Year   int      `json:"year"`  // 同上  Price  int      `json:"price"`  Actors []string `json:"actors"`  
}  func main() {  movie := Movie{  Title:  "喜剧之王",  Year:   2000,  Price:  10,  Actors: []string{"周星驰", "张柏芝"},  }  jsonStr, err := json.Marshal(movie) // 返回byte切片  if err != nil {  fmt.Println("json marshal error", err)  return  }  fmt.Printf("%s\n", jsonStr)  inputMovie := Movie{}  err = json.Unmarshal(jsonStr, &inputMovie)  if err != nil {  fmt.Println("json unmarshal error", err)  return  }  fmt.Printf("%v\n", inputMovie)  
}

输出:

{"title":"喜剧之王","year":2000,"price":10,"actors":["周星驰","张柏芝"]}
{喜剧之王 2000 10 [周星驰 张柏芝]}

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

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

相关文章

一次持续 15 年的网络安全“攻防之战”

2003 年 7-8 月&#xff0c;冲击波病毒爆发&#xff0c;对网络安全造成严重影响&#xff0c;尤其是校园网。冲击波病毒不仅使得校园网变得卡顿&#xff0c;还会在学生电脑上强制弹出一个倒计时窗口&#xff0c;60 秒后自动关机&#xff0c;给学生正常使用电脑和网络造成了极大不…

JS之递归

递归是什么&#xff1f; 调用自身函数称为递归函数 function fn(){fn()}fn()递归的作用和循环是基本一样的 编写递归函数&#xff0c;一定要包含两个条件 1.基线条件 2.递归条件 接下来我用几个实例为大家带来递归的用法 1.使用递归让延迟器有定时器的效果 function timer() …

Visual Studio Code (Vscode)配置LaTeX

Visual Studio Code (Vscode)配置LaTeX 实操记录 第一步高效检索&#xff0c;找到官方的、靠谱的安装教程&#xff0c;最好多找几个&#xff0c;英文、中文教程都需要 LaTeX WorkshopInstallation and basic settingsHow to install LaTeX (with previews & autocomplete…

Godot导出Android包报错:无效的包名称

问题描述 使用Godot为项目导出Android平台包时报错&#xff0c;提示&#xff1a;“无效的包名称&#xff1a;项目名称不符合包名格式的要求。请显式指定包名。” 解决办法 修改导出配置项“包->唯一名称”。 该项缺省值“org.godotengine.$genname”不能直接使用&#x…

Paper Reading: (ACRST) 基于自适应类再平衡自训练的半监督目标检测

目录 简介工作重点方法CropBankFBRAFFRTwo-stage Pseudo-label Filtering 实验与SOTA比较消融实验 简介 题目&#xff1a;《Semi-Supervised Object Detection with Adaptive Class-Rebalancing Self-Training》&#xff0c;AAAI’22&#xff0c; 基于自适应类再平衡自训练的半…

外汇天眼:心理素质决定交易成败!

在交易市场中&#xff0c;参与者非常多&#xff0c;成功的却极少。 成功的交易者几乎完全凭借个人的聪明才智&#xff0c;迎难而上&#xff0c;依靠顽强的毅力、坚韧不拔的性格以及冒险精神&#xff0c;战胜自己交易路上的一切挫折。 而这其中交易者的心理素质与其盈亏紧密相关…

【复现】vid2vid_zero

问题及解决方法总结。 code&#xff1a;GitHub - baaivision/vid2vid-zero: Zero-Shot Video Editing Using Off-The-Shelf Image Diffusion Models 1.AttributeError: UNet2DConditionModel object has no attribute encoder 据说是预训练模型结构不匹配&#xff0c;偷懒把a…

TLF35584 窗口看门狗_理论篇

1 功能描述 1)看门狗的结果(有效或无效触发)由相关的看门狗故障计数器独立监控。 2)窗口看门狗的状态是WWO,它可能有“有效WWD触发”或“无效WWD触发”值。 2 工作原理 1)在TLF35584中集成了看门狗对单片机进行监控。 2)被监视的微控制器必须在“打开窗口”内提供周期性…

架构师进阶,微服务设计与治理的 16 条常用原则

今天将从存储的上一层「服务维度」学习架构师的第二项常用能力 —— 微服务设计与治理。 如何设计合理的微服务架构&#xff1f; 如何保持微服务健康运行&#xff1f; 这是我们对微服务进行架构设计过程中非常关注的两个问题。 本文对微服务的生命周期定义了七个阶段&#x…

安装apisix详细教程

安装&#xff1a; docker安装ApiSi 常见问题-提前查阅 1-端口被占用 确保所需的所有端口&#xff08;默认的 9080/9091/9443/2379/9000&#xff09;未被其他系统/进程使用 #查询端口占用情况 netstat -antp |grep 9443 如果端口冲突可尝试修改apisix的端口配置&#xff0c; …

Vue 双向绑定:让数据与视图互动的魔法!(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

CentOS 7.x操作系统的ECS云服务器上搭建WordPress网站

WordPress是使用PHP语言开发的博客平台&#xff0c;在支持PHP和MySQL数据库的服务器上&#xff0c;您可以用WordPress架设自己的网站&#xff0c;也可以用作内容管理系统&#xff08;CMS&#xff09;。本教程介绍如何在CentOS 7.x操作系统的ECS实例上搭建WordPress网站。 背景…

<优化接口设计的思路>系列:留下用户调用接口的痕迹

目录 前言 方法一、将接口的参数和结果打印在日志文件中   1、使用aop监控接口   2、增加requestId   3、效果如下图   4、接口监控遇到的一些坑 方法二、将风险高的操作保存到数据库中   1、新建一张log表&#xff0c;存储风险操作   2、新建Log注解和切面处理类L…

springboot listener、filter登录实战

转载自&#xff1a; www.javaman.cn 博客系统访问&#xff1a; http://175.24.198.63:9090/front/index 登录功能 1、前端页面 采用的是layui-admin框架&#xff0c;文中的验证码内容&#xff0c;请参考作者之前的验证码功能 <!DOCTYPE html> <html lang"zh…

Java连接数据库的各种细节错误(细节篇)

目录 前后端联调&#xff08;传输文件&#xff09; ClassNotFoundException: SQLException: SQL语法错误: 数据库连接问题: 驱动问题: 资源泄露: 并发问题: 超时问题: 其他库冲突: 配置问题: 网络问题: SSL/TLS问题: 数据库权限问题: 驱动不兼容: 其他未知错误…

Docker-compose单机容器编排

YML文件是什么&#xff1f; YAML文件是一种标记语言&#xff0c;以竖列的形式展示序列化的数据格式。可读性很高类似于json格式。语法简单。 YAML通过缩进来表示数据结构&#xff0c;连续的项目用-符号来表示。 YML文件使用的注意事项 1、 大小写敏感 2、 通过缩进表示层级…

JavaScript实现飘窗功能

实现飘窗功能很简单 html代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title…

微信小程序 快速获取手机号 getphonenumber fail privacy permission is not authorized, errno: 104

getphonenumber fail privacy permission is not authorized, errno: 104 problem 小程序体验版 快速获取手机号注册 发现这个报错: getphonenumber fail privacy permission is not authorized, errno: 104 手头2个微信号可以测试1个微信号可以正常&#xff0c;另1个微信号…

Ubuntu22.04添加用户

一、查看已存在的用户 cat /etc/passwd 二、添加用户 sudo adduser xxx 除了密码是必须的&#xff0c;其他的都可以不填&#xff0c;直接回车即可 三、查看添加的用户 cat /etc/passwd 四、将新用户添加到sudo组 sudo adduser xxx sudo 五、删除用户 sudo delus…