Go语言之接口(interface)

news2024/11/29 21:29:42

1.1 、多态的含义

在java里,多态是同一个行为具有不同表现形式或形态的能力,即对象多种表现形式的体现,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

如下图所示:使用手机扫描二维码支付时,二维码并不知道客户是通过何种方式进行支付,只有通过二维码后才能判断是走哪种支付方式执行对应流程。

在这里插入图片描述

// 支付抽象类或者接口
 
public class Pay {
    public String pay() {
        System.out.println("do nothing!")
        return "success"
    }
}
// 支付宝支付
 
public class AliPay extends Pay {
    @Override
    public String pay() {
        System.out.println("支付宝pay");
        return "success";
    }
}
// 微信支付
 
public class WeixinPay extends Pay {
    @Override
    public String pay() {
        System.out.println("微信Pay");
        return "success";
    }
}

 // 银联支付
 
public class YinlianPay extends Pay {
    @Override
    public String pay() {
        System.out.println("银联支付");
        return "success";
    }
}

// 测试支付
public static void main(String[] args) {
    
    // 测试支付宝支付多态应用   
    Pay pay = new AliPay();
    pay.pay();
    // 测试微信支付多态应用    
    pay = new WeixinPay();
    pay.pay();
   // 测试银联支付多态应用  
    pay = new YinlianPay();
    pay.pay();
}

// 输出结果如下:

支付宝pay
微信Pay
银联支付

多态存在的三个必要条件:
继承
重写
父类引用指向子类对象

Pay pay = new AliPay();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

1.2、抽象类与接口

这样实现当然是可行的,但其实有一个小小的问题,就是Pay类当中的pay方法多余了。因为我们使用的只会是它的子类,并不会用到Pay这个父类。所以我们没必要实现父类Pay中的pay方法,做一个标记,表示有这么一个方法**,子类实现的时候需要实现它就可以了。

这就是抽象类和抽象方法的来源,我们可以把Pay做成一个抽象类,声明pay是一个抽象方法。抽象类是不能直接创建实例的,只能创建子类的实例,并且抽象方法也不用实现,只需要标记好参数和返回就行了。具体的实现都在子类当中进行。说白了抽象方法就是一个标记,告诉编译器凡是继承了这个类的子类必须要实现抽象方法,父类当中的方法不能调用。那抽象类就是含有抽象方法的类。

我们写出Pay变成抽象类之后的代码:

public abstract class Pay {
    abstract public String pay();
}

很简单,因为我们只需要定义方法的参数就可以了,不需要实现方法的功能,方法的功能在子类当中实现。由于我们标记了pay这个方法是一个抽象方法,凡是继承了Pay的子类都必须要实现这个方法,否则一定会报错。

抽象类其实是一个擦边球,我们可以在抽象类中定义抽象的方法也就是只声明不实现,也可以在抽象类中实现具体的方法。在抽象类当中非抽象的方法,子类的实例是可以直接调用的,和子类调用父类的普通方法一样。但假如我们不需要父类实现方法,我们提出提取出来的父类中的所有方法都是抽象的呢?针对这一种情况,Java当中还有一个概念叫做接口,也就是interface,本质上来说interface就是抽象类,只不过是只有抽象方法的抽象类。

所以刚才的Pay通过接口实现如下:

interface Pay {
    String pay();
}

把Pay变成了interface之后,子类的实现没什么太大的差别,只不过将extends关键字换成了implements。另外,子类只能继承一个抽象类,但是可以实现多个接口。早先的Java版本当中,interface只能够定义方法和常量,在Java8以后的版本当中,我们也可以在接口当中实现一些默认方法和静态方法。

接口的好处是很明显的,我们可以用接口的实例来调用所有实现了这个接口的类。也就是说接口和它的实现是一种要宽泛许多的继承关系,大大增加了灵活性。

以上虽然全是Java的内容,但是讲的其实是面向对象的内容,如果没有学过Java的小伙伴可能看起来稍稍有一点点吃力,但总体来说问题不大,没必要细扣当中的语法细节,get到核心精髓就可以了。

1.3、Go中的接口实现

Golang当中也有接口,但是它的理念和使用方法和Java稍稍有所不同,它们的使用场景以及实现的目的是类似的,本质上都是为了抽象。通过接口提取出了一些方法,所有继承了这个接口的类都必然带有这些方法,那么我们通过接口获取这些类的实例就可以使用了,大大增加了灵活性。

但是Java当中的接口有一个很大的问题就是侵入性,Golang当中的接口解决了这个问题,也就是说它完全拿掉了原本弱化的继承关系,只要接口中定义的方法能对应的上,那么就可以认为这个类实现了这个接口。

我们先来创建一个interface,当然也是通过type关键字:

type Pay interface {
    pay() string
}

我们定义了一个Pay的接口,当中声明了一个pay函数。也就是说只要是拥有这个函数的结构体就可以用这个接口来接收

type AliPay struct{}

type WeixinPay struct{}

type YinlianPay struct{}

func (a AliPay) pay() {
   fmt.Println("支付宝pay")
}

func (w WeixinPay) pay() {
   fmt.Println("微信pay")
}

func (y YinlianPay) pay() {
   fmt.Println("银联pay")
}

之后,我们尝试使用这个接口来接收各种结构体的对象,然后调用它们的pay方法:

func main() {
     var p Pay
     p = AliPay{}
     p.pay()
     p = WeixinPay{}
     p.pay()
     p = YinlianPay{}
     p.pay()
}

出来的结果是一样的!

golang中的接口设计非常出色,因为它解耦了接口和实现类之间的联系,使得进一步增加了我们编码的灵活度,解决了供需关系颠倒的问题。但是世上没有绝对的好坏,golang中的接口在方便了我们编码的同时也带来了一些问题,比如说由于没了接口和实现类的强绑定,其实也一定程度上增加了开发和维护的成本。

总体来说这是一个仁者见仁的改动,有些写惯了Java的同学可能会觉得没有必要,这是过度解绑,有些人之前深受其害的同学可能觉得这个进步非常关键。但不论你怎么看,这都不影响我们学习它,毕竟学习本身是不带立场的。

接口本身就是一种规范,能让大家在一个框架下开发,比如张三新进入部门,开发一个新的支付功能,在接口的限制下,开发就会会规范很多。就像USB接口一样,定义统一接口,无论外部实现的是音响还是硬盘,必须都按定义好的数据格式开发。

2.1、基本语法

在 Golang 中,interface 是一组 method 的集合,是 duck-type programming 的一种体现。不关心属性(数据),只关心行为(方法)。具体使用中你可以自定义自己的 struct,并提供特定的 interface 里面的 method 就可以把它当成 interface 来使用。
每个接口由数个方法组成,接口的定义格式如下:

type 接口类型名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2}

其中:
接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

2.2、实现接口的条件

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。
我们来定义一个Animal接口:

// Animal 接口
type Animal interface {
    sleep()
}

定义Dog和Cat两个结构体:

type Dog struct {
    name string
}
type Cat struct {
    name string
}

因为Animal接口里只有一个sleep方法,所以我们只需要给Dog和Cat 类分别实现sleep方法就可以实现Sayer接口了。

// Dog实现了Animal接口
func (d Dog) sleep() {
    fmt.Printf("%s正在侧卧着睡觉\n", d.name)
}
// Cat实现了Animal接口
func (c Cat) sleep() {
    fmt.Printf("%s正在卷成团睡觉\n", c.name)
}

接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

2.3、接口类型变量

那实现了接口有什么用呢?
接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Animal类型的变量能够存储Dog和Cat类型的变量。


func foo(animal Animal)  {
    animal.sleep()
}

func main() {
    var a Animal

    var d = Dog{"川普"}
    var c = Cat{"拜登"}

    // 案例1
    a = d
    a.sleep()
    a = c
    a.sleep()
    // 案例2
    foo(d)
    foo(c)
}

2.4、值和指针接收者实现接口

使用值接收者实现接口和使用指针接收者实现接口有什么区别呢?接下来我们通过一个例子看一下其中的区别。

(1)值接收者实现接口

package main

import (
    "fmt"
)

type Animal interface {
    sleep()
}

type Dog struct {
    name string
}

// Dog实现了Animal接口
func (d Dog) sleep() {
    fmt.Printf("%s正在侧卧着睡觉\n", d.name)
}

func main() {
    var a Animal
    var chuanPu = Dog{"川普"}
    a = chuanPu     // a可以接收Dog类型
    chuanPu.sleep() // 将Dog类型chuanPu拷贝给接收者方法sleep的d,然后执行sleep方法
    a = &chuanPu    // a可以接受*Dog类型
    a.sleep()       //将*Dog类型chuanPu取值操作后拷贝给接收者方法sleep的d,然后执行sleep方法

}

从上面的代码中我们可以发现,使用值接收者实现接口之后,不管是Dog结构体对象还是结构体指针对象都可以赋值给该接口变量。因为Go语言中有对指针类型变量求值的语法糖,Dog指针a内部会自动求值川普结构体对象然后拷贝赋值。

(2)指针接收者实现接口

同样的代码我们再来测试一下使用指针接收者有什么区别:

package main

import (
    "fmt"
)

type Animal interface {
    sleep()
}

type Dog struct {
    name string
}

// Dog实现了Animal接口
func (d *Dog) sleep() {
    fmt.Printf("%s正在侧卧着睡觉\n", d.name)
}

func main() {
    var a Animal
    var chuanPu = Dog{"川普"}
    // a = chuanPu     // a不可以接收Dog类型
    a = &chuanPu      // a只可以接收*Dog类型
    a.sleep()       

}

此时实现Animal的接口的是Dog类型,所以不能给a传入Dog类型的chuanPu,此时a只能存储Dog类型的值,即&chuanPu。

2.5、类型与接口的关系

(1)一个类型实现多个接口

一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。 例如,狗可以跑,也可以叫。我们就分别定义Runner接口和Sayer接口。

// Sayer 接口
type Sayer interface {
    say()
}

// Runner 接口
type Runner interface {
    run()
}

Dog既可以实现Sleep接口,也可以实现Run接口。

package main

import (
    "fmt"
)

// Sayer 接口
type Sayer interface {
    say()
}

// Runner 接口
type Runner interface {
    run()
}

type Dog struct {
    name string
}

// 实现Sayer接口
func (d Dog) say() {
    fmt.Printf("%s汪汪汪叫\n", d.name)
}

// 实现Runner接口
func (d Dog) run() {
    fmt.Printf("%s吐舌头跑\n", d.name)
}

func main() {
    var s Sayer
    var r Runner

    var d = Dog{name: "旺财"}
    s = d
    s.say()
    r = d
    r.run()
}

(2)多个类型实现同一接口

Go语言中不同的类型还可以实现同一接口 首先我们定义一个Runner接口,它要求必须有一个run方法。

// Runner 接口
type Runner interface {
    run()
}

例如狗可以跑,汽车也可以跑,可以使用如下代码实现这个关系:

type Car struct {
    brand string
}
type Dog struct {
    name string
}


// Runner 接口
type Runner interface {
    run()
}


// Dog实现Runner接口
func (d Dog) run() {
    fmt.Printf("%s正在吐舌头跑\n", d.name)
}

// Car实现Runner接口
func (c Car) run() {
    fmt.Printf("%s正在飞速行驶\n", c.brand)
}

这个时候我们在代码中就可以把狗和汽车当成一个会动的物体来处理了,不再需要关注它们具体是什么,只需要调用它们的move方法就可以了。

func main() {
    var r Runner

    var d = Dog{name: "旺财"}
    var c = Car{brand: "奔驰"}
    r = d
    r.run()
    r = c
    r.run()
}

2.6、类型嵌套

一个类型(struct)必须实现了接口中的所有方法才能称为实现了该接口。

// Animal 接口
type Animal interface {
    sleep()
    run()
}

type Dog struct {
    name string
}

// Dog实现了run方法和sleep方法,即实现了Animal接口
func (d Dog) run() {
    fmt.Printf("%s正在吐舌头跑\n", d.name)
}
/*func (d Dog) sleep() {
    fmt.Printf("%s正在侧翻睡\n", d.name)
}*/


func main() {
    var r Animal

    var d = Dog{name: "旺财"}
    r = d
    r.run()
}

一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。

package main

import (
    "fmt"
)

// WashingMachine 洗衣机
type WashingMachine interface {
    wash()
    dry()
}

// 甩干器
type Dryer struct{
    brand string
}

// 实现WashingMachine接口的dry()方法
func (d Dryer) dry() {
    fmt.Println("甩干衣服")
}

// 海尔洗衣机
type Haier struct {
    name string
    Dryer //嵌入甩干器
}

// 实现WashingMachine接口的wash()方法
func (h Haier) wash() {
    fmt.Println("洗衣服")
}


func main() {
    var wm WashingMachine
    wm = Haier{
        name: "海尔洗衣机",
        Dryer:Dryer{
            brand:"西门子",
        },
    }

    wm.wash()
    wm.dry()
}

2.7、接口嵌套

接口与接口间可以通过嵌套创造出新的接口。

package main

import (
    "fmt"
)

// Animal 接口
/*type Animal interface {
    sleep()
    run()
}*/


// Sleep接口
type Sleep interface {
    sleep()
}
// Runner接口
type Runner interface {
    run()
}

// Animal接口
type Animal interface {
    Sleep
    Runner
}

type Dog struct {
    name string
}

// Dog实现了run方法和sleep方法,即实现了Animal接口
func (d Dog) run() {
    fmt.Printf("%s正在吐舌头跑\n", d.name)
}
func (d Dog) sleep() {
    fmt.Printf("%s正在侧翻睡\n", d.name)
}


func main() {
    var r Animal
    var d = Dog{name: "旺财"}
    r = d
    r.run()
    r.sleep()
}

2.8、空接口

(1)空接口的定义

空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。

func main() {
    // 定义一个空接口x
    var x interface{}
    s := "Hello Yuan"
    x = s
    fmt.Printf("type:%T value:%v\n", x, x)
    i := 100
    x = i
    fmt.Printf("type:%T value:%v\n", x, x)
    b := true
    x = b
    fmt.Printf("type:%T value:%v\n", x, x)
}

(2)空接口的应用

空接口作为函数的参数
使用空接口实现可以接收任意类型的函数参数。

// 空接口作为函数参数
func show(a interface{}) {
    fmt.Printf("type:%T value:%v\n", a, a)
}

空接口作为map的值
使用空接口实现可以保存任意值的字典。

// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "yuan"
studentInfo["age"] = 18
studentInfo["isMarried"] = false
fmt.Println(studentInfo)

类型断言
一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。这两部分分别称为接口的动态类型和动态值。
想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:

x.(T)

其中:

x:表示类型为interface{}的变量
T:表示断言x可能是的类型。
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

举个例子:

func main() {
    var x interface{}
    x = "Hello Yuan!"
    v, ok := x.(string)
    if ok {
        fmt.Println(v)
    } else {
        fmt.Println("类型断言失败")
    }
}

上面的示例中如果要断言多次就需要写多个if判断,这个时候我们可以使用switch语句来实现:

func justifyType(x interface{}) {
    switch v := x.(type) {
    case string:
        fmt.Printf("x is a string类型,value is %v\n", v)
    case int:
        fmt.Printf("x is a int类型, value is %v\n", v)
    case bool:
        fmt.Printf("x is a bool类型,value is %v\n", v)
    default:
        fmt.Println("unsupport type!")
    }
}

func main() {
    justifyType(12)
    justifyType(true)
    justifyType("hi,yuan!")
}

因为空接口可以存储任意类型值的特点,所以空接口在Go语言中的使用十分广泛。
关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。

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

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

相关文章

T5模型: Transfer Text-to-Text Transformer(谷歌)

🔥 T5由谷歌发表于2019,《Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer》,最终版本发布在:JMLR。 一句话总结T5: 大一统模型,seq2seq形式完成各类nlp任务,大数据集…

Docker 的前世今生:从社区到市场,从领域到技术应用的全方位分析

博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客👦🏻 《java 面试题大全》 🍩惟余辈才疏学浅,临摹之作或有不妥之处,还请读者海涵指正。☕🍭 《MYSQL从入门到精通》数据库是开发者必会基础之…

下载编译Chromium

参考:Mac上本地编译Chrome浏览器踩坑笔记(2021.02最新) - 掘金 For Mac: 一、下载编译工具链:deptool git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH"$PATH:/Users/yumlu/co…

jib进行本地打包,并上传本地镜像仓库

使用 Jib 进行本地打包和上传到本地镜像仓库是一种方便的方式,而无需编写 Dockerfile。Jib 是一个开源的 Java 容器镜像构建工具,它可以直接将 Java 项目打包为镜像,并将其推送到容器镜像仓库。 gradle 进行jib的配置 import java.time.Zon…

第53步 深度学习图像识别:Bottleneck Transformer建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 (1)Bottleneck Transformer "Bottleneck Transformer"(简称 "BotNet")是一种深度学习模型,在2021年由Google的研究人员在论文"Bottleneck Transfor…

MaxCompute与 Mysql 之单字段转多行

在实际数据处理中,可能会遇到行列转换的数据处理,在 MaxCompute 与 AnalyticDB MySQL 数据处理与转换 介绍过如多行转一行,本篇主要介绍将逗号分割的字段转成多行。 一、MaxCompute 实现方式 在MaxCompute中有TRANS_ARRAY函数,可…

显示一行或两行多出的文字用省略号代替

以上就是一行的效果,超出宽度就用...代替 .recommendContainer .scrollItem text{/* 单行文本溢出隐藏 省略号代替 */display: block;white-space: nowrap; /*溢出不换行*/overflow: hidden; /*溢出隐藏*/text-overflow: ellipsis; /*溢出的内容已...代替*/} 多…

watch中监听vuex中state改变监听不到

watch中监听vuex中state改变监听不到 https://blog.csdn.net/aliven1/article/details/100581529?utm_mediumdistribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-100581529-blog-122614448.t5_layer_targeting_sa&spm1001.2101.3001.4242…

软通动力与华秋达成生态共创合作,共同推动物联网硬件创新

7月11日,在2023慕尼黑上海电子展现场,软通动力信息技术(集团)股份有限公司(以下简称“软通动力”)与深圳华秋电子有限公司(以下简称“华秋”)签署了生态共创战略合作协议,共同推动物联网硬件生态繁荣发展。当前双方主要基于软通动力的产品及解…

从Vue2到Vue3【二】——Composition API(第二章)

系列文章目录 内容链接从Vue2到Vue3【零】Vue3简介及创建从Vue2到Vue3【一】Composition API(第一章) 文章目录 系列文章目录前言一、 生命周期二、hook三、toRef以及toRefs总结 前言 Vue3作为Vue.js框架的最新版本,引入了许多令人激动的新…

vue项目部署自动检测更新

前言 当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。 在以往解决方案中,不少人会使用websocke…

C#基础--委托

C#基础–委托 C#基础–委托 简单说它就是一个能把方法当参数传递的对象,而且还知道怎么调用这个方法,同时也是粒度更小的“接口”(约束了指向方法的签名) 一、什么是委托,委托的本质是什么? 跟方法有点类似,有参数,返回值,访问修饰符+ delegate public delegate void …

Python 算法基础篇:哈希表与散列函数

Python 算法基础篇:哈希表与散列函数 引用 1. 哈希表的概念2. 散列函数的概念 a ) 一致性 b ) 均匀性 c ) 高效性 3. 散列函数的实现4. 哈希表的实现5. 哈希表的冲突解决 a ) 链地址法 b ) 开放地址法 6. 实例演示实例:电话簿 总结 引用 哈希表是一种高…

[计算机入门] 文件夹(目录)及路径

2.7 文件夹(目录)及路径 文件夹(folder、目录)是一种用于存储和组织文件和其他文件夹的容器。它可以包含任意数量的文件和子文件夹,并且可以通过拖放、复制和粘贴等操作来移动、复制和管理这些文件和子文件夹。 Windows文件夹可以帮助用户更…

spring复习:(43)使用TransactionProxyFactoryBean来实现事务时,事务是怎么开启的?

一、配置文件&#xff1a; <bean id"myFactoryBean"class"org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name"transactionManager" ref"transactionManager" /><property nam…

可以替代微软 Exchange 的 几个开源软件分享给你

微软Exchange是一个功能强大的邮件和群件解决方案&#xff0c;但对于一些用户来说&#xff0c;寻找替代方案可能是必要的。幸运的是&#xff0c;有几个开源软件提供了可靠而且功能丰富的替代选项。这些开源软件不仅可以满足组织和个人的邮件和协作需求&#xff0c;还具有灵活性…

quartus18.0如何下载安装Cyclone V器件库

文章目录 前言一、下载流程二、添加步骤三、总结四、参考资料 前言 在我们使用不同版本的板子的时候&#xff0c;我们需要在quartus下安装不同型号的器件库才能对板子进行选型并进行下一步操作。 一、下载流程 官网下载地址 这里我们点击支持选中下载中心&#xff1a; 选择FPGA…

reggie优化04-Nginx

官方网站下载&#xff1a;http://nginx.org/en/download.html 1、Nginx安装 这里需要在Linux系统下&#xff1a; 安装wget工具&#xff1a;yum install wget&#xff08;或者官网下载直接上传到Linux&#xff09; 安装树形结构tree&#xff1a;yum install tree 2、Nginx命令 …

在云计算环境中,保护Java应用程序可用的有效措施和工具

云计算&#xff08;Cloud&#xff09;技术是近年来计算机科学的一个重要突破。大多数组织已经通过将自己的应用程序移入云平台而获益。不过&#xff0c;如何保证应用程序在第三方服务器上的安全性&#xff0c;是一项艰巨的挑战。 在本文中&#xff0c;我们将重点讨论Java&…

Notepad++ 配置python虚拟环境(Anaconda)

Notepad配置python运行环境步骤&#xff1a; 打开Notepad ->”运行”菜单->”运行”按钮在弹出的窗口内输入以下命令&#xff1a; 我的conda中存在虚拟环境 (1) base (2) pytorch_gpu 添加base环境至Notepad中 cmd /k chdir /d $(CURRENT_DIRECTORY) & call cond…