15分钟学 Go 第 51 天 :通用库与工具使用

第51天:通用库与工具使用

一、学习目标

类别工具/库用途
命令行工具cobra构建命令行应用
JSON处理gjson高效JSON解析
HTTP客户端restyHTTP请求处理
日期处理carbon时间日期操作
配置管理viper配置文件处理

二、详细实现

让我们通过具体示例来学习这些库的使用:

// main.go
package mainimport ("fmt""log""time""github.com/go-resty/resty/v2""github.com/spf13/cobra""github.com/spf13/viper""github.com/tidwall/gjson""github.com/golang-module/carbon/v2"
)// 配置结构
type Config struct {APIEndpoint string `mapstructure:"api_endpoint"`APIKey      string `mapstructure:"api_key"`Timeout     int    `mapstructure:"timeout"`
}var (cfgFile stringconfig  Config
)// rootCmd represents the base command
var rootCmd = &cobra.Command{Use:   "toolapp",Short: "A demo application showing various Go tools",Long:  `This application demonstrates the usage of various Go libraries and tools.`,
}// getDataCmd represents the getData command
var getDataCmd = &cobra.Command{Use:   "getdata",Short: "Get data from API",Run: func(cmd *cobra.Command, args []string) {// 创建HTTP客户端client := resty.New()client.SetTimeout(time.Duration(config.Timeout) * time.Second)// 发送请求resp, err := client.R().SetHeader("Authorization", "Bearer "+config.APIKey).Get(config.APIEndpoint)if err != nil {log.Fatalf("Request failed: %v", err)}// 使用gjson解析响应result := gjson.Parse(resp.String())// 提取数据items := result.Get("data.items")items.ForEach(func(key, value gjson.Result) bool {fmt.Printf("Item: %s\n", value.Get("name"))// 解析时间timestamp := value.Get("created_at").String()t := carbon.Parse(timestamp)fmt.Printf("Created: %s (Humanized: %s)\n", t.ToDateTimeString(), t.DiffForHumans())return true})},
}// timeCmd represents the time command
var timeCmd = &cobra.Command{Use:   "time",Short: "Show time operations",Run: func(cmd *cobra.Command, args []string) {now := carbon.Now()fmt.Println("Current time operations:")fmt.Printf("DateTime: %s\n", now.ToDateTimeString())fmt.Printf("Date: %s\n", now.ToDateString())fmt.Printf("Time: %s\n", now.ToTimeString())fmt.Printf("Timestamp: %d\n", now.Timestamp())// 时间计算fmt.Println("\nTime calculations:")future := now.AddDays(7)fmt.Printf("7 days later: %s\n", future.ToDateTimeString())fmt.Printf("Human readable: %s\n", future.DiffForHumans())// 时间判断fmt.Println("\nTime comparisons:")fmt.Printf("Is future? %v\n", future.IsAfter(now))fmt.Printf("Is weekend? %v\n", now.IsWeekend())},
}func init() {cobra.OnInitialize(initConfig)rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./config.yaml)")rootCmd.AddCommand(getDataCmd)rootCmd.AddCommand(timeCmd)
}func initConfig() {if cfgFile != "" {viper.SetConfigFile(cfgFile)} else {viper.SetConfigName("config")viper.SetConfigType("yaml")viper.AddConfigPath(".")}viper.AutomaticEnv()if err := viper.ReadInConfig(); err == nil {fmt.Println("Using config file:", viper.ConfigFileUsed())}if err := viper.Unmarshal(&config); err != nil {log.Fatalf("Unable to decode into config struct, %v", err)}
}func main() {if err := rootCmd.Execute(); err != nil {log.Fatal(err)}
}

配置文件示例:

# config.yaml
api_endpoint: "https://api.example.com/data"
api_key: "your-api-key"
timeout: 30

让我们创建一个流程图来说明程序的执行流程:
在这里插入图片描述
让我们添加一个HTTP服务器的示例:

package mainimport ("encoding/json""log""net/http""time""github.com/gin-gonic/gin""github.com/golang-module/carbon/v2"
)type Item struct {Name      string    `json:"name"`CreatedAt time.Time `json:"created_at"`
}type Response struct {Data  struct {Items []Item `json:"items"`} `json:"data"`
}func main() {r := gin.Default()// 添加中间件r.Use(gin.Logger())r.Use(gin.Recovery())// 设置路由r.GET("/data", func(c *gin.Context) {// 创建示例数据now := carbon.Now()items := []Item{{Name:      "Item 1",CreatedAt: now.SubDays(2).ToStdTime(),},{Name:      "Item 2",CreatedAt: now.SubDays(1).ToStdTime(),},{Name:      "Item 3",CreatedAt: now.ToStdTime(),},}response := Response{}response.Data.Items = itemsc.JSON(http.StatusOK, response)})// 启动服务器if err := r.Run(":8080"); err != nil {log.Fatal("Failed to start server:", err)}
}

三、工具库详解

1. Cobra命令行工具

功能说明示例
命令定义定义命令及其参数cobra.Command
子命令支持命令嵌套rootCmd.AddCommand()
参数绑定绑定命令行参数cmd.Flags()
自动补全命令自动补全cmd.ValidArgsFunction

2. Viper配置管理

功能说明示例
配置读取支持多种配置格式viper.ReadInConfig()
环境变量支持环境变量覆盖viper.AutomaticEnv()
配置监控配置文件热重载viper.WatchConfig()
配置绑定绑定到结构体viper.Unmarshal()

3. Resty HTTP客户端

功能说明示例
请求发送支持各种HTTP方法client.R().Get()
重试机制请求失败自动重试client.SetRetryCount()
超时控制设置请求超时client.SetTimeout()
中间件请求/响应拦截器client.OnBeforeRequest()

4. Carbon时间处理

功能说明示例
时间解析支持多种格式解析carbon.Parse()
时间计算时间加减操作carbon.AddDays()
时间比较比较时间大小carbon.IsAfter()
人性化显示友好的时间显示carbon.DiffForHumans()

5. GJSON JSON处理

功能说明示例
路径查询支持复杂路径查询result.Get("data.items")
类型转换自动类型转换result.Int()
数组处理遍历JSON数组result.ForEach()
错误处理安全的值获取result.Exists()

四、最佳实践

1. 命令行应用开发建议

  • 使用子命令组织功能
  • 提供详细的帮助信息
  • 支持配置文件
  • 实现命令自动补全

2. HTTP客户端使用建议

  • 复用HTTP客户端实例
  • 设置适当的超时时间
  • 实现重试机制
  • 处理错误响应

3. 配置管理建议

  • 支持多环境配置
  • 使用环境变量覆盖
  • 实现配置验证
  • 支持配置热重载

4. 时间处理建议

  • 统一时间格式
  • 考虑时区问题
  • 使用易读的时间表示
  • 正确处理时间计算

五、进阶用法

1. 中间件实现

让我们实现一个简单的中间件示例:

package mainimport ("fmt""log""net/http""time""github.com/gin-gonic/gin""github.com/golang-module/carbon/v2"
)// 请求日志中间件
func RequestLogger() gin.HandlerFunc {return func(c *gin.Context) {// 开始时间startTime := time.Now()// 处理请求c.Next()// 计算处理时间duration := time.Since(startTime)// 记录请求日志log.Printf("[%s] %s %s %d %v",c.Request.Method,c.Request.URL.Path,c.ClientIP(),c.Writer.Status(),duration,)}
}// API认证中间件
func APIAuth() gin.HandlerFunc {return func(c *gin.Context) {apiKey := c.GetHeader("Authorization")if apiKey == "" {c.JSON(http.StatusUnauthorized, gin.H{"error": "Missing API key",})c.Abort()return}// 这里可以添加实际的API key验证逻辑if apiKey != "Bearer your-api-key" {c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key",})c.Abort()return}c.Next()}
}// 错误恢复中间件
func ErrorRecovery() gin.HandlerFunc {return func(c *gin.Context) {defer func() {if err := recover(); err != nil {log.Printf("Panic recovered: %v", err)c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error",})}}()c.Next()}
}// 限流中间件
func RateLimit(limit int, duration time.Duration) gin.HandlerFunc {type client struct {count    intlastSeen time.Time}clients := make(map[string]*client)return func(c *gin.Context) {ip := c.ClientIP()now := time.Now()if clients[ip] == nil {clients[ip] = &client{}}if now.Sub(clients[ip].lastSeen) > duration {clients[ip].count = 0clients[ip].lastSeen = now}if clients[ip].count >= limit {c.JSON(http.StatusTooManyRequests, gin.H{"error": "Rate limit exceeded",})c.Abort()return}clients[ip].count++clients[ip].lastSeen = nowc.Next()}
}func main() {r := gin.New() // 不使用默认中间件// 应用自定义中间件r.Use(ErrorRecovery())r.Use(RequestLogger())r.Use(RateLimit(100, time.Minute)) // 限制每分钟100个请求// API路由组api := r.Group("/api")api.Use(APIAuth()) // 对API路由应用认证中间件api.GET("/data", func(c *gin.Context) {// 创建示例数据now := carbon.Now()data := []map[string]interface{}{{"id":         1,"name":      "Item 1","created_at": now.SubDays(2).ToDateTimeString(),},{"id":         2,"name":      "Item 2","created_at": now.SubDays(1).ToDateTimeString(),},}c.JSON(http.StatusOK, gin.H{"data": data,"meta": map[string]interface{}{"total":    2,"page":     1,"per_page": 10,},})})// 健康检查路由r.GET("/health", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"status":  "healthy","version": "1.0.0","time":    carbon.Now().ToDateTimeString(),})})// 启动服务器if err := r.Run(":8080"); err != nil {log.Fatal("Failed to start server:", err)}
}

2. 并发控制示例

让我们实现一个带并发控制的数据处理示例:

package mainimport ("context""fmt""log""sync""time""golang.org/x/time/rate"
)// Worker 工作池
type Worker struct {id       intjobs     chan Joblimiter  *rate.LimiterresultCh chan<- Result
}// Job 任务定义
type Job struct {id   intdata interface{}
}// Result 结果定义
type Result struct {jobID     intworkerID  intdata      interface{}processed boolerror     error
}// WorkerPool 工作池管理器
type WorkerPool struct {workers   []*Workerjobs      chan Jobresults   chan Resultlimiter   *rate.Limiterwg        sync.WaitGroup
}// NewWorkerPool 创建工作池
func NewWorkerPool(workerCount int, jobBuffer int, rateLimit float64) *WorkerPool {pool := &WorkerPool{workers:   make([]*Worker, workerCount),jobs:      make(chan Job, jobBuffer),results:   make(chan Result, jobBuffer),limiter:   rate.NewLimiter(rate.Limit(rateLimit), 1),}// 创建工作协程for i := 0; i < workerCount; i++ {worker := &Worker{id:       i + 1,jobs:     make(chan Job, 1),limiter:  rate.NewLimiter(rate.Limit(rateLimit/float64(workerCount)), 1),resultCh: pool.results,}pool.workers[i] = workergo worker.start()}return pool
}// Start 启动工作池
func (wp *WorkerPool) Start(ctx context.Context) {// 分发任务go func() {for {select {case <-ctx.Done():returncase job := <-wp.jobs:// 选择一个worker处理任务workerIndex := job.id % len(wp.workers)worker := wp.workers[workerIndex]select {case worker.jobs <- job:case <-ctx.Done():return}}}}()
}// Submit 提交任务
func (wp *WorkerPool) Submit(job Job) {wp.wg.Add(1)wp.jobs <- job
}// Wait 等待所有任务完成
func (wp *WorkerPool) Wait() {wp.wg.Wait()close(wp.jobs)close(wp.results)
}// GetResults 获取结果通道
func (wp *WorkerPool) GetResults() <-chan Result {return wp.results
}// Worker方法
func (w *Worker) start() {for job := range w.jobs {// 限流控制err := w.limiter.Wait(context.Background())if err != nil {w.resultCh <- Result{jobID:     job.id,workerID:  w.id,processed: false,error:     err,}continue}// 处理任务result := w.process(job)w.resultCh <- result}
}func (w *Worker) process(job Job) Result {// 模拟处理时间time.Sleep(100 * time.Millisecond)return Result{jobID:     job.id,workerID:  w.id,data:      fmt.Sprintf("Processed data for job %d by worker %d", job.id, w.id),processed: true,error:     nil,}
}func main() {// 创建工作池pool := NewWorkerPool(5, 100, 10.0) // 5个工作协程,缓冲100个任务,每秒处理10个请求ctx, cancel := context.WithCancel(context.Background())defer cancel()// 启动工作池pool.Start(ctx)// 提交任务for i := 1; i <= 20; i++ {job := Job{id:   i,data: fmt.Sprintf("Task %d", i),}pool.Submit(job)}// 收集结果go func() {for result := range pool.GetResults() {if result.error != nil {log.Printf("Job %d failed: %v", result.jobID, result.error)} else {log.Printf("Job %d completed by worker %d: %v",result.jobID, result.workerID, result.data)}pool.wg.Done()}}()// 等待所有任务完成pool.Wait()
}

3. 配置热重载示例

package mainimport ("fmt""log""sync""time""github.com/fsnotify/fsnotify""github.com/spf13/viper"
)// AppConfig 应用配置结构
type AppConfig struct {Server struct {Port     int    `mapstructure:"port"`Host     string `mapstructure:"host"`LogLevel string `mapstructure:"log_level"`} `mapstructure:"server"`Database struct {Host     string `mapstructure:"host"`Port     int    `mapstructure:"port"`User     string `mapstructure:"user"`Password string `mapstructure:"password"`DBName   string `mapstructure:"dbname"`} `mapstructure:"database"`Redis struct {Host     string `mapstructure:"host"`Port     int    `mapstructure:"port"`Password string `mapstructure:"password"`DB       int    `mapstructure:"db"`} `mapstructure:"redis"`
}// ConfigManager 配置管理器
type ConfigManager struct {config     *AppConfigconfigLock sync.RWMutexonUpdate   []func(*AppConfig)
}// NewConfigManager 创建配置管理器
func NewConfigManager() *ConfigManager {return &ConfigManager{config:   &AppConfig{},onUpdate: make([]func(*AppConfig), 0),}
}// LoadConfig 加载配置
func (cm *ConfigManager) LoadConfig(configPath string) error {viper.SetConfigFile(configPath)viper.AutomaticEnv()if err := viper.ReadInConfig(); err != nil {return fmt.Errorf("failed to read config file: %w", err)}// 初始加载配置if err := cm.updateConfig(); err != nil {return err}// 监听配置文件变化viper.WatchConfig()viper.OnConfigChange(func(e fsnotify.Event) {log.Printf("Config file changed: %s", e.Name)if err := cm.updateConfig(); err != nil {log.Printf("Failed to reload config: %v", err)}})return nil
}// updateConfig 更新配置
func (cm *ConfigManager) updateConfig() error {var newConfig AppConfigif err := viper.Unmarshal(&newConfig); err != nil {return fmt.Errorf("failed to unmarshal config: %w", err)}cm.configLock.Lock()cm.config = &newConfigcm.configLock.Unlock()// 通知所有监听器for _, callback := range cm.onUpdate {callback(&newConfig)}return nil
}// GetConfig 获取当前配置
func (cm *ConfigManager) GetConfig() *AppConfig {cm.configLock.RLock()defer cm.configLock.RUnlock()return cm.config
}// OnConfigUpdate 注册配置更新回调
func (cm *ConfigManager) OnConfigUpdate(callback func(*AppConfig)) {cm.onUpdate = append(cm.onUpdate, callback)
}// 示例服务器结构
type Server struct {config *AppConfigcm     *ConfigManager
}// NewServer 创建服务器
func NewServer(cm *ConfigManager) *Server {return &Server{config: cm.GetConfig(),cm:     cm,}
}// Start 启动服务器
func (s *Server) Start() error {// 注册配置更新回调s.cm.OnConfigUpdate(func(newConfig *AppConfig) {s.onConfigUpdate(newConfig)})// 模拟服务器运行for {s.printConfig()time.Sleep(5 * time.Second)}
}// onConfigUpdate 处理配置更新
func (s *Server) onConfigUpdate(newConfig *AppConfig) {log.Println("Server received new configuration")s.config = newConfig
}// printConfig 打印当前配置
func (s *Server) printConfig() {cfg := s.configlog.Printf("Server Configuration:")log.Printf("Host: %s, Port: %d, LogLevel: %s",cfg.Server.Host,cfg.Server.Port,cfg.Server.LogLevel)log.Printf("Database: %s@%s:%d/%s",cfg.Database.User,cfg.Database.Host,cfg.Database.Port,cfg.Database.DBName)log.Printf("Redis: %s:%d/DB%d",cfg.Redis.Host,cfg.Redis.Port,cfg.Redis.DB)
}func main() {// 创建配置管理器cm := NewConfigManager()// 加载配置文件if err := cm.LoadConfig("config.yaml"); err != nil {log.Fatal(err)}// 创建并启动服务器server := NewServer(cm)if err := server.Start(); err != nil {log.Fatal(err)}
}

配置文件示例:

# config.yaml
server:port: 8080host: "localhost"log_level: "info"database:host: "localhost"port: 5432user: "postgres"password: "secret"dbname: "myapp"redis:host: "localhost"port: 6379password: ""db: 0

4. 实用工具函数集

让我们创建一个常用工具函数的集合:

package utilsimport ("crypto/md5""encoding/hex""encoding/json""fmt""math/rand""reflect""regexp""strings""time"
)// StringUtils 字符串工具
type StringUtils struct{}// RandomString 生成随机字符串
func (u StringUtils) RandomString(length int) string {const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))b := make([]byte, length)for i := range b {b[i] = charset[seededRand.Intn(len(charset))]}return string(b)
}// MD5 计算MD5哈希
func (u StringUtils) MD5(text string) string {hasher := md5.New()hasher.Write([]byte(text))return hex.EncodeToString(hasher.Sum(nil))
}// IsEmail 验证邮箱格式
func (u StringUtils) IsEmail(email string) bool {pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`reg := regexp.MustCompile(pattern)return reg.MatchString(email)
}// SliceUtils 切片工具
type SliceUtils struct{}// Contains 检查切片是否包含元素
func (u SliceUtils) Contains(slice interface{}, elem interface{}) bool {arrV := reflect.ValueOf(slice)if arrV.Kind() != reflect.Slice {return false}for i := 0; i < arrV.Len(); i++ {if reflect.DeepEqual(arrV.Index(i).Interface(), elem) {return true}}return false
}// Unique 去除切片中的重复元素
func (u SliceUtils) Unique(slice interface{}) interface{} {arr := reflect.ValueOf(slice)if arr.Kind() != reflect.Slice {return slice}seen := make(map[interface{}]bool)result := reflect.MakeSlice(arr.Type(), 0, arr.Len())for i := 0; i < arr.Len(); i++ {elem := arr.Index(i).Interface()if !seen[elem] {seen[elem] = trueresult = reflect.Append(result, arr.Index(i))}}return result.Interface()
}// MapUtils Map工具
type MapUtils struct{}// MergeMap 合并多个map
func (u MapUtils) MergeMap(maps ...map[string]interface{}) map[string]interface{} {result := make(map[string]interface{})for _, m := range maps {for k, v := range m {result[k] = v}}return result
}// TimeUtils 时间工具
type TimeUtils struct{}// FormatDuration 格式化持续时间
func (u TimeUtils) FormatDuration(d time.Duration) string {days := int(d.Hours() / 24)hours := int(d.Hours()) % 24minutes := int(d.Minutes()) % 60seconds := int(d.Seconds()) % 60parts := make([]string, 0)if days > 0 {parts = append(parts, fmt.Sprintf("%dd", days))}if hours > 0 {parts = append(parts, fmt.Sprintf("%dh", hours))}if minutes > 0 {parts = append(parts, fmt.Sprintf("%dm", minutes))}if seconds > 0 || len(parts) == 0 {parts = append(parts, fmt.Sprintf("%ds", seconds))}return strings.Join(parts, " ")
}// JsonUtils JSON工具
type JsonUtils struct{}// PrettyPrint 格式化JSON字符串
func (u JsonUtils) PrettyPrint(data interface{}) (string, error) {bytes, err := json.MarshalIndent(data, "", "    ")if err != nil {return "", err}return string(bytes), nil
}// Utils 工具集合
type Utils struct {String StringUtilsSlice  SliceUtilsMap    MapUtilsTime   TimeUtilsJSON   JsonUtils
}// NewUtils 创建工具集实例
func NewUtils() *Utils {return &Utils{String: StringUtils{},Slice:  SliceUtils{},Map:    MapUtils{},Time:   TimeUtils{},JSON:   JsonUtils{},}
}// 使用示例
func ExampleUsage() {utils := NewUtils()// 字符串工具randomStr := utils.String.RandomString(10)fmt.Println("Random string:", randomStr)md5Hash := utils.String.MD5("hello world")fmt.Println("MD5 hash:", md5Hash)isEmail := utils.String.IsEmail("test@example.com")fmt.Println("Is valid email:", isEmail)// 切片工具numbers := []int{1, 2, 2, 3, 3, 4}uniqueNumbers := utils.Slice.Unique(numbers)fmt.Println("Unique numbers:", uniqueNumbers)// Map工具map1 := map[string]interface{}{"a": 1, "b": 2}map2 := map[string]interface{}{"c": 3, "d": 4}merged := utils.Map.MergeMap(map1, map2)fmt.Println("Merged map:", merged)// 时间工具duration := 36*time.Hour + 15*time.Minute + 45*time.Secondformatted := utils.Time.FormatDuration(duration)fmt.Println("Formatted duration:", formatted)// JSON工具data := map[string]interface{}{"name": "John","age":  30,"hobbies": []string{"reading","gaming",},}prettyJSON, _ := utils.JSON.PrettyPrint(data)fmt.Println("Pretty JSON:", prettyJSON)
}

六、总结

在本课程中,我们学习了以下内容:

  1. 常用工具库的使用:

    • Cobra:命令行应用开发
    • Viper:配置管理
    • Resty:HTTP客户端
    • Carbon:时间处理
    • GJSON:JSON处理
  2. 实践示例:

    • 命令行工具开发
    • HTTP服务器实现
    • 中间件开发
    • 并发控制
    • 配置热重载
    • 工具函数集
  3. 最佳实践:

    • 代码组织结构
    • 错误处理
    • 性能优化
    • 测试策略

怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

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

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

相关文章

基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自…

VLC-QT----Linux编译并运行示例

linux:ubuntu 16.04 qt:5.13.2 总体安装步骤 下载安装,编译 下载源码仓库,下载cmake,新建一个build文件夹,cd进去,执行代码 cmake .. -DCMAKE_BUILD_TYPEDebug 遇到报错,没有qt5Coreconfig,运行 sudo apt-get install qtdeclarative5-dev进行安装 遇到报错 Could not fi…

机器学习:XGBoost模型——高效且强大的树形模型

XGBoost&#xff08;Extreme Gradient Boosting&#xff0c;极端梯度提升树&#xff09;是一种强大的梯度提升算法&#xff0c;在现实中被广泛用于分类和回归任务。它通过集成多个简单的基学习器&#xff08;通常是决策树&#xff09;来构建一个强大的预测模型。 基本原理步骤…

爬虫开发工具与环境搭建——开发工具介绍

第二章&#xff1a;爬虫开发工具与环境搭建 第一节 开发工具介绍 爬虫开发需要一些合适的工具和框架来高效地抓取网页数据。在这节中&#xff0c;我们将介绍常用的开发工具&#xff0c;帮助开发者快速搭建爬虫开发环境。 1. Python与爬虫框架选择 Python因其简洁、易学的语法…

python高级之面向对象编程

一、面向过程与面向对象 面向过程和面向对象都是一种编程方式&#xff0c;只不过再设计上有区别。 1、面向过程pop&#xff1a; 举例&#xff1a;孩子上学 1. 妈妈起床 2. 妈妈洗漱 3. 妈妈做饭 4. 妈妈把孩子叫起来 5. 孩子起床 6. 孩子洗漱 7. 孩子吃饭 8. 妈妈给孩子送学校…

通过Docker实现openGauss的快速容器化安装

容器安装 本章节主要介绍通过 Docker 安装 openGauss&#xff0c;方便 DevOps 用户的安装、配置和环境设置。 支持的架构和操作系统版本 x86-64 CentOS 7.6 ARM64 openEuler 20.03 LTS 配置准备 使用 buildDockerImage.sh 脚本构建 docker 镜像&#xff0c;buildDockerIm…

康谋分享 | 确保AD/ADAS系统的安全:避免数据泛滥的关键

为确保AD/ADAS系统的安全性&#xff0c;各大车企通常需要收集、处理和分析来自于摄像头、激光雷达等传感器的数据&#xff0c;以找出提高系统安全性和性能的方法。然而在数据收集过程中&#xff0c;不可避免地会出现大量无价值数据&#xff0c;造成数据泛滥的情况&#xff0c;进…

电工电子原理笔记

这一篇手记会记录我硬件开发过程中遇到的一些底层电学原理&#xff0c;并且结合实际场景作为“例题”&#xff08;出于篇幅和保密考虑会进行部分简化&#xff09;。 叠加定理 基本介绍 在线性电路中&#xff0c;任一支路的电流&#xff08;或电压&#xff09;可以看成是电路…

【赵渝强老师】MySQL InnoDB的段、区和页

MySQL的InnoDB存储引擎的逻辑存储结构和Oracle大致相同&#xff0c;所有数据都被逻辑地存放在一个空间中&#xff0c;我们称之为表空间&#xff08;tablespace&#xff09;。表空间又由段&#xff08;segment&#xff09;、区&#xff08;extent&#xff09;、页&#xff08;pa…

Python 继承笔记

知识点&#xff1a; 1.has a 一个类中使用了另外一种自定义类的类型 student 使用computer book 2.类型 系统类型 str,int,float,list,tuple,dic,set 自定义类型 算是自定义的类&#xff0c;都可以将其当成一种类型 student是一种类型 sStudent() s是Student的类型 class Stud…

Vue3 -- 项目配置之husky【企业级项目配置保姆级教程4】

引言&#xff1a; eslint&#xff1a;代码规范校验prettier&#xff1a;代码格式化stylelint&#xff1a;CSS代码校验 上述三篇文章集成配置完成代码校验工具&#xff0c;当时需要每次手动的去执行命令才会格式化我们的代码。。如果有人没有格式化就提交了远程仓库&#xff0…

万字长文分析函数式编程

目录 一.认识函数式编程 一、函数式编程的定义 二、函数式编程的思想 三、函数式编程的特点 四、函数式编程的应用 二.Lambda表达式 三.Stream流 3.1 创建流对象 3.2 注意事项 3.3 Stream流的中间操作 filter map distinct sorted limit skip flatMap 3.4 St…

移植 AWTK 到 纯血鸿蒙 (HarmonyOS NEXT) 系统 (8) - 原生输入法

AWTK 在嵌入式平台使用内置的输入法&#xff0c;在移动设备上使用系统的原生输入法。在 AWTK-Android 和 AWTK-IOS 中&#xff0c;使用的是 SDL 封装之后的系统原生输入法。在 AWTK-HarmonyOS 中&#xff0c;要使用系统的原生输入法。需要实现 input_method 接口&#xff1a; 1…

【解决】Layout 下创建槽位后,执行 Image 同步槽位位置后表现错误的问题。

开发平台&#xff1a;Unity 6.0 编程语言&#xff1a;CSharp 编程平台&#xff1a;Visual Studio 2022   一、问题背景 | 开发库存系统 图1 位置同步失败问题 图2 位置正常同步效果表现 黑框 作用于 UnityEngine.UI.GridLayoutGruop&#xff0c;形成 4x6 布局&#xff0c;如…

【Jenkins实战】Windows安装服务启动失败

写此篇短文&#xff0c;望告诫后人。 如果你之前装过Jenkins&#xff0c;出于换域账号/本地帐号的原因想重新安装&#xff0c;你大概率会遇上一次Jenkins服务启动失败提示&#xff1a; Jenkins failed to start - Verify that you have sufficient privileges to start system…

免费,WPS Office教育考试专用版

WPS Office教育考试专用版&#xff0c;不仅满足了考试需求&#xff0c;更为教育信息化注入新动力。 https://pan.quark.cn/s/609ef85ae6d4

94个属于一区且接受医工交叉领域投稿的期刊汇总|个人观点·24-11-13

小罗碎碎念 继汇总病理AI的基础模型、病理组学&影像组学的公开数据集以后&#xff0c;我们再来盘一盘医工交叉领域有哪些热门期刊可以投稿。我会分区进行介绍&#xff0c;每个区则会进一步划分学科种类&#xff0c;方便大家选择适合自己的投稿期刊。 这期推文先分享大类属…

【插件】重复执行 pytest-repeat

安装 pip3 install pytest-repeat 用法 1.命令行 pytest --count num pytest --count 32.装饰器 pytest.mark.repeat(num) #num运行次数 pytest.mark.repeat(5)#执行结果如下&#xff1a;

el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现

el-table 中有现成的隔行换色功能&#xff0c;只要增加 stripe 属性即可。但是如果有单元格合并的话&#xff0c;这个属性就不可用了。这时候我们就需要动点小心思了。 基于相同字段进行合并 单元格合并&#xff1a;基于表头中的某一列&#xff0c;具有相同值的个数相加进行合…

【小白玩NAS】PVE硬盘直通

简介 在DAS架构中&#xff0c;硬盘&#xff08;NvMe除外&#xff09;通过硬盘控制器连接并由其管理。因此&#xff0c;如果将硬盘控制器直通到虚拟机&#xff0c;控制器下的所有硬盘也会间接直通至虚拟机。这样一来&#xff0c;虚拟机内会将这些硬盘视为物理磁盘&#xff0c;并…