V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 3 页 / 共 10 页
回复总数  186
1  2  3  4  5  6  7  8  9  10  
@sagaxu #19

你提到“我们”的时候,还是没有跳出固有的思维。

在 Go 的设计思想里,包或者说库的作者,应当( should )假定自己的包有一天可能成为别人的依赖。然而你并不需要假想别人要以何种方式使用你的包,你只管写你的实现完成功能就可以了。别人可能仅仅因为一个结构定义,或者一个导出方法就引用你的包作为依赖。

如果我自己要同时完成某个功能的 A/B/C 三个实现,我当然会提前把接口写好。但是如果我只有 A 的需求,那我完全可以不写接口,写成接口的形式是为了某一天我要添加 B/C 支持、或者我知道这个包可能被别人拿去用而做的预先设计。

无论如何,受限于 Java 的类型检查机制,如果最初没有写成接口,任何人除了包的作者都没有很简单的办法复用一个没有接口的包,因为把接口抽象出来这个操作只能由包的所有者完成。虽然你觉得原作者对于 A 功能的实现写得很好,但想拿过来用,才不得不选择复制粘贴,否则就要求助原包的作者将代码改成接口的形式。复制粘贴的问题是一旦上游更新,你就会面临是不是要手动跟随更新的问题。提 PR 是考虑到,减轻原作者的工作量,提高原作者接口化的意愿,避免你自己跟随更新的麻烦。

这两个都是在尽量减少对原包作者的依赖,但是在人的层面解耦不够彻底。
Reply

上面解释得可能还不是很清晰,我加一点代码来说明吧,还是以对象存储支持两个后端为例。由于回复不支持 markdown ,所以手动排版了一下。



1.
第一个非接口的版本:

```java
// A.java 实现功能的部分
class A {
____void get();
____void put();
}

// main.java 调用的部分
class main {
____void use(a A);
}
```

这个版本如果别人引用去,是很难添加 B 服务商支持的。除非是复制粘贴拿来用,但如果是复杂的项目,要么只能长期手动维护,要么向上游提 PR 。

所以包作者往往会以 preemptive 的形式,改成接口的版本,方便下游使用:

```java
// A.java
interface Storage {
____void get();
____void put();
}
class A implements Storage { ... }

// main.java
class main {
____void use(s Storage);
}
```

这样下游的人可以自己去适配另一个类 B 来实现 Storage 接口了。

```java
// B.java
import A.Storage;
class B implements Storage { ... }
```

这样做的问题是,如果 Storage 接口方法非常多(正常云服务 sdk 少说都有二三十个方法),那么 B 也要适配同样数量的方法。实际上下游适配 B 可能仅仅需要 get/put 两个方法而已,这对于开发和测试都是非常不利的。



2.
再来看看 Go 的初始非接口版本:

```go
// package A
type A struct { ... }
func (a *A) Get() {}
func (a *A) Put() {}

// package main
func Use(a *A)
```

然后上游作者就什么都不用管了。下游用户看到,想要增加 B 支持:

```go
// package B
type B struct { ... }
func (b *B) Get() {}
func (b *B) Put() {}

// package main
type Storage interface {
____Get()
____Put()
}
type MyStorage struct {}
func NewStorage(s Storage) *MyStorage { ... }
// 没有写成 func Use(s Storage) 是为了体现后半句 return structs ,这个不重要
```

其他不用管了。假如上游发布了更新,只要 Get/Put 接口签名不变(即上有发布的新版 API 向后兼容),那就可以直接升级使用。整个过程里,上游下游的工作是完全独立的。

即便考虑现实中 Storage 有几十个接口,下游用户也只需要实现他所用到的少量方法即可。



二者区别其实是在于:谁来定义接口。Java 的机制决定了只能是 producer 说了算,而 Go 则是 consumer 主导。这样看 preemptive 这个词的意义就很清晰了:还不知道会被怎么用的情况下就把接口定好了,当然是“抢占”了,实际上 Go 这里根本没必要预先定义,等用到的时候再写就是了,用多少写多少。
我刚好在写一模一样主题的文章,完成后会发上来。起因可以看我最近回复,当时我评价某个项目的代码“不能正确使用接口解耦”。

如果你在学习 Go 之前没有太多编程经验,这句话对你来说可能非常自然,自然到令人疑惑,因为你不知道反过来做是什么样子的。如果你的思维模型受 Java 的影响很深,那么理解这句话才能理解 Go 在解耦方面带来的巨大进步。(不抬杠,这里以 Go 和 Java 做对比纯粹因为最方便理解)

要说清楚这个问题需要比较大的篇幅,我这里简单概括一下。

1.
Go/Rust 这类现代语言都放弃了“继承”,这是设计思想的巨大进步,Java 这种旧时代的语言在设计的时候是没有意识到“组合优于继承”的。

理解组合优于继承对于中国人来说非常简单,汉语只需要几千个字就能描述整个宇宙,除此之外的其他语言的,那句台词怎么说的,不是针对谁,在座的诸位……

2.
基于组合的理念之上的 OO 抽象,才产生了 Accept Interfaces, return structs 这个 Go idiomatic 的范式。

我比较认同 Rob Pike 对这句话的评价,它不够准确,也不够精确。如果让我来表述,我会分成两句话:

- The bigger the interface, the weaker the abstraction. 这一句是纯引用作为铺垫,意在表达接口越小越好。

- Don't implement Interfaces preemptively. Preemptive 这个词一般翻译成“抢占式”,这里取其衍生含义,提前或者预先。Java 实现接口的代码范式就是 preemptive 的。

3.
Go 的隐式接口实现和 Java 显式接口实现,根本区别在于 Go 能够以接口为边界,从工程层面将开发工作解耦。

举个例子,你开发了一个库,功能是对象存储中间件,它支持以 A 厂商云存储作为后端,实现了 get/put 的读写方法。

如果有另外一个人需要增加对 B 厂商的支持:

- 用 Go 的话,他只需要引用你的包,然后定义一个包含 get/put 方法的接口,同时将 B 厂商的 sdk 做封装即可。调用的时候直接以接口为参数,而不需要关注具体实现接口的对象。

-用 Java 来实现的话,他要么把你的包以源码的形式复制一遍加入到项目里,要么就要向你提 PR ,来增加对 B 厂商的支持。这是因为 class B implements StorageInterface 只能写在你的包里。

这里就看出 Go 的先进之处了,你要做什么和原包的作者没有关系,原包的作者也不需要关心其他人是怎么用他的包的。而 Java 世界里,要么把上游的人拉进来,要么自己成为上游。代码上好像解耦了,但又没完全解耦,工程上是非常低效率的事情。

为了解决这个麻烦,Java 就有了 Preemptive 实现接口的惯例。考虑到需要增加适配,写类的时候有事没事先写个接口出来,不管用得到用不到。

但这样做的问题是,接口会变得巨大无比。一般的对象存储服务,少说也有二三十个方法。一个有二三十个接口的方法是什么概念?当你需要 mock 一下写测试的时候就有得受了,事实上你可能只会用到 get/put 两个接口而已。

然后 Java 提了一个叫 SOLID 的原则,其中 I 代表接口隔离,意思是要把接口拆分。问题是作为库的作者,你只有一次拆分机会,而包的使用者却有数不清的排列组合。


PS
补充几句题外话,我在 Go 语言主题里的回复经常会被人说是踩 Java 捧 Go ,但我依旧坚持有理有据地论证,而不是停留在嘴巴或者屁股上。

很容易看出来,如果设计思想落后了,想要模仿先进的东西是非常困难的。我不止一次重复过,考虑到 Java/Python 这些语言诞生的时间,从设计层面上评价它们落后是不公平的,毕竟这些语言作为先驱踩了坑,才会有后来现代语言的设计指导思想。

另外需要指出的是,Go 基于 duck typing 的隐士式接口的范式是少数不能通过语法糖等方式在 Java 中实现的机制。在一个静态类型语言上实现 weakly typed 的特性( duck typing ),Go 应该算是第一个。
32 天前
回复了 nmap 创建的主题 程序员 求一个文件 N 点同步的方案
TLDR

不知道你考虑过冲突解析的问题?同一时间两个节点上的同一文件产生了不同的变化,要以哪个副本为准?如果存在这种情况,那你要解决的实际上是分布式系统的同步问题,方案会非常复杂。比如一般的对象存储,都是传一个副本,然后节点之间完成副本同步。

如果不存在这种情况,可以降级成非分布式问题,方案也简单很多。比如你只是需要多个备份,同一时间只会在一个节点上操作。这个情况比较简单的做法是选一个节点作为权威副本,其他的节点都以这个节点为准,向权威副本推送更新的时候要先合并权威副本。类似于使用 github 之类的平台进行协作的模式。rsync 加上简单脚本就能实现。


----手动分割----

这个问题没有普适的答案,本质上它是受分布式系统 CAP 理论限制的。需要确认需求,进而在 CAP 三者当中选二,然后才能确认方案。由于绝大多数情况下,P 是不能放弃的,所以要么只能 AP 要么只能 CP 。

AP 方案放弃 C ,结果就是某一个时刻,各个节点之间的副本有的是最新版,有的是旧版。

CP 方案放弃 A ,在任意同步行为完成之前,不能进行其他操作。

实际应用里最先考虑的是降级,把分布式降级为星型,就是上面说的权威副本节点。如果无法降级,那就需要使用基于 paxos/raft 这类共识算法的同步机制。
34 天前
回复了 solywsh 创建的主题 Linux 有没能按照 ip 进行流量统计的
这个需求也算是 V2EX 上的常客了,我详细说一下思路和方向。

Linux 网络栈是在内核里实现的,流量统计就是 packet 计数(或者计量)这个计数有几个方式:

1. 内核态 pf 用户态 iptables

最简单的方式就是 iptables 对 chain 计数,只要把想统计的流量走一下自定义的 chain (这个 chain 可以什么都不做),就可以利用 chain 自身计数达到目的。

像 openwrt 之类的路由器统计内网流量基本都是这个方式,用 arp 配合 MAC 地址来区分客户端。

这个方式的优点是轻量,缺点是要预先设定规则。你的这个场景,如果几个朋友的 IP 有比较明显的区分度,可以根据 IP 段每个人走一个规则 chain 来统计。但是对于大量无规律来源 IP 就很难写这个规则,或者写出来几万条不实际。

2. conntrack

想要动态处理 ip 规则,就需要做基于状态的统计。conntrack 有自己维护一套映射表,这样就无需预先知道来源 ip 就可以按需统计。优点是灵活性比较高,但是性能影响也会比上一个方法高一些。

这两个方法的实现可以参考 https://openwrt.org/docs/guide-user/services/network_monitoring/bwmon 这个帖子 Available tools 章节,可以参考拿来用。

第一个方案其实很好写,第二个要想自己写可能需要比较多的背景知识。

3. 抓包自己算

就是计量的方式,不是很推荐的做法,为了做流量统计结果把所有数据包都过滤一遍,有点杀鸡用牛刀的意思了。
34 天前
回复了 pegasusz 创建的主题 程序员 戴尔内存泄漏漏洞?还是恶意攻击?
做个无责任推测,如果很多人同时发生类似的事情,可能是这个程序有个访问特定服务器的定时任务,然后服务器挂了导致程序无限高频重试,进而放大了内存泄漏、cpu 占用等问题。
36 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@AbrahamKindle #38

我不太确定你所说的性能指的是哪个方面,我就随便说一下。

原文里谈到 Go 不适合音频视频处理指的是生态问题。比如图片领域有 imagemagick 音频视频领域有 ffmpeg ,通常没有重新造轮子的。如果要用到 Go 项目里就需要通过 FFI 来调用,这就需要通过 CGO 的方式。如果没有人开发相应的 binding ,那就要通过命令行直接调用了。这两个都不是很理想的方式。

音视频处理领域谈性能,一般应该是实时领域吧,指标是延迟和 jitter 这种。粗略来说,不仅仅是 Go ,凡是带 GC 的语言都不适合干这个事情,因为没有完全控制 GC 介入行为的方法,GC 介入会短暂中断程序的运行,这对实时应用来说很难接受。当然这个事情不绝对,通过一些手动管理对象、内存或者 zero copy 流式处理的方式说不定也能满足性能需求,就是回到 C 那种思路上面。

另外音频视频处理涉及的范围很广,以我有限的经验来说:

- 涉及底层硬件的尽量还是用 C ,写 binding 一点都不好玩
- 调用个摄像头做 cv 识别这种是可以的,因为你对接的不是硬件而是 v4l2 之类的抽象,opencv 的 golang binding 也够用
- 走 opengl 写个 shader 什么的也还好,但是基本没法对接到一般游戏开发工作流里面

所以说还是尽量用对应领域的工具吧。
抱着解答问题的心态点进来,看见楼上的评论就笑了。名著你可以揣测一下作者的写作思路,地摊文学就算了……

代码层面的问题 @bv 在楼上已经说得比较清楚了,我补充一点我的理解。

这种代码水平在我之前的面试标准里面,按照初级/中级/高级三档水平,只能划分到初级那一档。我只通过看链接里那个文件就做出了判断,而且对此我非常有把握。

划分到初级的主要理由是:无法准确使用接口 Interface 来实现功能解耦。这个能力在我之前负责面试的时候是中级技能里的。相比之下,代码层面反倒是小问题了。

如果看不懂的话,我可以抽时间单独就 Go 通过接口来解耦专门做个解释。有隔壁那个代码氛围的帖子在,其实不想过多评价。但我还是要说,这段代码的作者的 Go 水平真就是初学者,出来卖课就是误人子弟了。
38 天前
回复了 zoeyx 创建的主题 程序员 前端 Coder 如何学习 Golang?
语法层面过一遍官方的 Tour 差不多够了。

练手的话我比较推荐尝试写个爬虫服务,不是让你真去爬什么,而是这个过程用到的东西学习曲线比较平滑。

解析页面元素这个过程,大概能熟悉强类型语言处理字符串的模式,了解结构体的应用。后期还会接触到反射等机制的应用。

之后是一般的网络编程,发送接收请求。这个过程可以熟悉标准库的风格和惯例,网络库算是 Go 比较精髓的部分了。

再之后是多线程处理,了解 chan/goroutine 的使用,以及常见的并发模型。领会一下用通信的方式来共享内存的核心思维。

最后把改造成服务,学习一下路由处理、中间件等等服务端常见的应用。之后有可能会慢慢接触到模板、泛型的应用。

前期不用考虑处理太复杂的情形,用标准库把功能实现出来就行。整个过程里可以慢慢熟悉后端工程化的实践。
38 天前
回复了 saranz 创建的主题 V2EX 好奇,至今还在 V2EX 活跃的 ID 是那些。
一般差 20 岁就是一代人,V2EX 从 10 算已经 15 年多,当年的种子用户可能都已经三十多岁了。

我估计有一批人应该和我一样,只是老账号各种原因不用了但是人还在。
38 天前
回复了 nobject 创建的主题 Go 编程语言 golang 日志记录
我不确定我的做法是不是 idiomatic 只是简单描述一下我的思路。毕竟 slog 进标准库也没多久,看 api 也是受 zap 影响很大。

大的逻辑方面我比较倾向于全局只有一个唯一 logger ,中间调用过程只传递。另外我习惯把业务层面的日志和功能实现层面的日志分开,然后两者用不同的方式来处理。

业务层面的日志我倾向于用格式化的字符串:一方面它是给运维用的,需要一眼看明白在哪个环节出了问题,但并不需要知道出问题的细节;另一方面这类日志要进日志服务器做后续统计等功能,需要方便处理。

对应的就是你的描述里 controller->service->repo 这个部分,我会用 errors 传递字符串,每个环节用 errorf 来 wrap 上一个环节的错误,起到简单的 stack trace 的功能。这里一般不建议用 runtime 来获取完整的 stack trace ,主要是性能开销的问题。

包或者实现层面的日志,一般是给开发者方便调试用的,比如看到底是什么入参触发了什么样的边界条件导致出错。这个信息在开发和测试环节比较有用,实际线上是相对冗余的信息。

日志记录这个信息我用的是自定义的 context ,因为标准库里的 context 实际上只有下文没有上文。可以看这个帖子 https://s.v2ex.com/t/1012453 我的回复。

用 context 的话可以自定义数据结构,能够比较好处理那些复杂的结构体,也就是你说的第二条里的麻烦。

对于 context 信息的记录就没有必要走业务层面的 logger 了。对于不太敏感又注重性能的业务,我的处理是加一个开关,当线上出现的问题无法复现的时候,开启开关再记录完整的 runtime stack trace 信息。对于相对敏感的业务,会单独用一个 logger 导出到日志服务器单独的表中,使用 traceId 对业务层面的日志和实现层面的日志做个关联。业务层面的日志要保留很久,但功能层面的日志一般三个月就可以清理了。
38 天前
回复了 afxcn 创建的主题 Go 编程语言 使用 go 遇到的一个奇怪问题,求教
对于 rand.NewSource 存在竞态导致 panic 这一点应该没有异议,而 panic 没有中断而是正常被调用,说明上层应该是存在 recover 逻辑的。结合 OP #23 指出本地正常而测试服务器异常,推测本地测试的生成逻辑,而服务器完成的是全部调用逻辑。

@R18 #16 指出 byte 空值转换为 string 之后是空字符串,说明在可见代码的部分,只可能产生空字符串。 @AceGo 和 @hopingtop 判断出是上层调用将空字符串变成了全 0 字符串。

我这里做一点补充,一般要保证字符串定长都会做 padding ,用字符串 0 做填充是最常见的。
39 天前
回复了 Livid 创建的主题 V2EX 站点状态 20240505 - 邀请码系统
我觉得金币机制的数值设计可能需要调整一下,至于如何调整我没什么思路,这个调整的影响并不是单一的。

我这个号是特意注册的,主要是回复解答问题,算是内容质量相对较高的了吧(自认为)。简单统计了一下,不到一年时间:

- 长回复(超过 5 消耗)大概 40 次,总消耗 650 左右
- 收到感谢大约 250 次,获取 2600 左右

目前余额 8500 ,其中来自于高质量内容获取(感谢)净值只有 2000 ,其他均为自动签到或者活跃度获取的。

相对来说,目前的数值设计对于活跃度的鼓励远高于内容产出。
41 天前
回复了 0312birdzhang 创建的主题 奇思妙想 关于打包箱子引发的思考
可以参考这个装箱问题 wiki 上面 Bin_packing_problem

放到现实世界里,搬家这个场景一般都是一个固定大箱子,往里面放各种小箱子的问题。可能还会有更多约束,比如重量,一箱子书换谁都搬不动。所以人靠直觉一般能很快找到一个可行但不一定最优的方案,换作程序里可能就是经验或者启发式算法。

另外大箱子也是可以变形的,比如 50cm 长 30cm 宽,切割一下胶带一封就可以变成 40x40 或者 60x20 来用。
42 天前
回复了 unclemcz 创建的主题 Ubuntu snap 已经在污染 apt
如果你在用 Ubuntu ,同时你又不认可 Canonical 的做法,我建议你考虑一下 Debian ,它是 Ubuntu 的上游发行版。

Debian 的全称是 Debian GNU/Linux ,如果不清楚什么是 GNU 可以看官方 FAQ https://www.debian.org/doc/manuals/debian-faq/basic-defs.en.html#whatisdebian

至于原因,我这里引用一下 Richard Stallman 于 1985 年写的 GNU Manifesto 中的一段话 https://www.gnu.org/gnu/manifesto.html

- Why All Computer Users Will Benefit
Users will no longer be at the mercy of one programmer or company which owns the sources and is in sole position to make changes.
42 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@lolizeppelin #77

如果只是单纯辩论美丑,我原本没打算回复,但既然你提到了 python 用装饰器来减少代码,那我觉得还是可以讨论一下的。

生命周期管理这个事情的困难在于:资源的分配和释放可能时机不确定,又或者在代码中的位置不同,还可能是多线程的主体不确定。原地释放几乎没有人会写错,而 scope 变得很大的时候,错写或者漏写的几率就会变大。

考虑 with...as 加入到 python 中已经有二十年了,但是装饰器版本的 contextmanager 也就十年,异步安全的版本可能最近才实现。这里有个很现实的问题,作为使用者是更愿意手写 enter/exit 还是愿意手动管理?当 contextmanager 加入的时候,项目允许升级吗?当有异步、多线程需求的时候,开发者能写出正确的版本吗?

所以抛开美丑的主观看法不谈,defer 这种万金油几乎是个 Go 开发者就会写,而 with...as 使用率就要大打折扣。先让人用起来显然是重要地多的事情。这里不抬杠开发者水平高低的问题,defer 是一种兜底机制,当开发者不确定我应该在什么时候释放资源的时候就可以用,而 with...as 即便是装饰器的版本显得繁琐,写好几行肯定比不上写一个单词,这是人性。



之前的回复里我总是反复提,不要单独拿某个关键词来讨论特性,那就以 with...as 来彻底完整做个说明,证明一下为什么这样做不合适。

C/Python/Java 由于诞生较早,都是先有的实现,然后反过来总结出来的标准。比较新的语言,都是设计先于实现,终归会从之前语言中总结经验教训,所以不仅仅是 Go ,你会发现 C#/Rust/Kotlin 这些“现代”语言往往有些共性,共享的是新的设计理念。



这里 defer/with...as 反映的其实是对异常处理这件事的哲学思考不同。

传统语言中,一方面异常是不区分错误和 Panic 的,另一方面异常又与控制流强耦合,try...catch 实质是隐式的 goto 指令。

现代语言几乎都放弃了这样的思路,Panic 要立即终止,而错误是可以处理的。代码层面也尽量避免隐式跳转。这样的改变是大趋势,原因是大家都从 C 语言中汲取了足够的教训,隐式控制流是有害的( hidden control flow considered harmful ),特别是与 C 的宏机制一结合,人脑 debug 几乎变得不可能。

反过来,显式控制流的优点是显而易见的,一方面极大提高了代码的可读性以及可靠性,所见即所得,不存在那些写在其他地方的代码被意外调用执行的情况;另一方面对于并行编程的支持变得更加容易。

如果拿 Go 来举例可能没有说服力,我简单说下 Rust 和 Kotlin 。

- Rust 没有 exception ,只有 recoverable/unrecoverable 错误,类型分别为 Result<T, E> 和 panic! 只有后者是宏实现的跳转

- Kotlin 保留了 try 关键词,但 try 是表达式而非(控制流)语句,也就是说 try 可以返回(错误)值

在减轻开发者心智负担方面,新的设计哲学可谓是殊途同归。这个进步主要体现在工程化层面,当语言不区分错误和 Panic 的时候,开发者会主动选择最无脑的方式,也就是随意抛出集中处理。现代语言中砍掉了这样的机制,反过来倒逼开发者思考异常处理的本质,从而间接提高代码的可复用性,以及程序的可靠性。



回到 with...as 的问题上,Python 的错误处理还是传统的控制流模式。换句话说,就算语法糖写出花来,它的核心仍旧是基于 enter/exit 的对于 RAII 的模仿。由于 Python 没有 RAII ,这种模仿的结果就是一定要手动处理 scope 的问题。

先说一个比较接近最终方案的提案版本:

with EXPR:
____BLOCK

这个版本看上去相当美好,可是没多久就被枪毙了。原因有两个,一是 with 只能是 statement 而不能是表达式(需要同时支持 with VAR = EXPR: 这样的语法,VAR = EXPR 赋值本身也是合法的 EXPR );二是 EXPR 自身有可能再次抛出异常,或者包含 break/continue 等其他控制流指令,结果会导致 scope 失效。

第二条麻烦一下解释器也不是不可以,多维护一个基于栈的 trackback 机制。但第一条的影响是致命的:

with VAR = EXPR:
____BLOCK

想要对以上形式写装饰器,__exit__ 一定要在 VAR 上有定义,这就失去了装饰器省代码的意义。

为了不让 VAR 获得 EXPR 的赋值,最终实现的版本用 with EXPR [as VAR]: 这样的形式。相关实现细节 PEP 都能搜到,这里就不继续展开了。

即便是用上装饰器,代码大概是这样的:

@contextmanager
def open(file):
____f = open(file)
____try:
________yield f
____finally:
____f.close()

写这么多只是为了实现:可以在 with scope 里,不用单独写 f.close()。

装饰器的实现晚于 with...as 很久才进标准库,核心原因就是前面提到的隐式控制流。装饰器本身

客观地说,Python 本来就是试错型快速迭代偏好的语言,with...as 加上迭代器的语法糖的作用非常有限。它起到的主要作用是,当你知道什么时候该写 f.close() 的时候( scope 边界清晰),不用你再写了(自定义类型还是要写装饰器)。但是解决不了辅助开发者判断,什么时候该写的问题,你无法依赖解释器帮你兜底。



尽管在站队好或者不好的事情上,网友可能会支持你也可能会反对你,但不代表大家都是出于相同的推理逻辑。在语言设计这么关键的问题上,一半是妥协一半是取舍,单拿出来一个点做比较最终只会变成屁股之争。
42 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@lolizeppelin #75

确实我说 Go 好不好对 Go 和其他编程语言没有什么意义,但不代表我在这里的回帖没有意义。

很多人觉得我是狂热的吹捧者,我想还是有所误解。这里面有我表述方式的原因,也有读者立场的原因。我在之前的某个帖子里表达过,我大多数回复的目的是“创造价值”,因为我从 V2EX 学到了很多,我也想把我学到的反馈给社区。Go 的主题比较多更多是因为它有一定流行度却又不那么流行,在我日常工作里,主流的语言应用比例里 Go 反倒没有那么高。

关于 python 的 with 语法,我有个问题,你怎么看自定义对象需要手写 __enter__ __exit__ 的?

单独拿 with 出来,配合 python 的缩进,起到了看上去很美的效果。但是没有 RAII ,为了实现 scope ,又要回到手写析构的路上。

因此我一直都强调,单独拿一个关键词一个特性出来比较是不合适的。
42 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
@KinsleyNg #9

我是说不推荐 fedora ,ubuntu 什么情况不清楚。非官方打包的只能叫 chromium ,试一下官方源里的吧。
44 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
@KinsleyNg #7

实测 xwayland 有效,wayland 无效。楼上说 24.1 相关补丁才合并,目前 f40/rawhide 都只有 24.0.6 的包。

即便补丁合并了,我还是不建议以 chrome 为主力的时候选择 fedora 。由于版权的缘故,RH 系只能把解码器托管在 rpmfusion ,然后 chromium build 和原版经常不一致。反正 gtk4 在我印象里是没有生效过的。

然后 chromium 只实现了 text-input-v1 协议,这个协议除了 KDE/KWin 根本没人支持,就连 KWin 的支持还是 fcitx5 作者加回去的。所以在 Fedora 上既要 wayland 又想输入法正常,只能是 KDE 版本。而其他发行版可以 gnome/gtk4 曲线救国。

另外我只是用 fedora 但是不用 gnome ,日常主力是 sway ,sway 不支持 text-input-v1 。hyprland 那边是支持的,fedora/hyprland 这个组合可以考虑。

至于 firefox 虽然我常年支持,但是在性能方面弱不少,硬件解码支持大家都一样,但其他方面差距很大。我在另一个帖子里刚好提到了,B 站现在那个弹幕功能,firefox 的硬件加速比较原始,纯 3D overlay 。Chrome 就好很多,把 css3 用 video enhance 单元来加速。
1  2  3  4  5  6  7  8  9  10  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2749 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 38ms · UTC 03:40 · PVG 11:40 · LAX 20:40 · JFK 23:40
Developed with CodeLauncher
♥ Do have faith in what you're doing.