如何透过现象看本质,真正理解“类”这个概念?

Hlianbobo 14天前 7

最近看 python 的书,其中讲了一些关于面向对象和类的知识。我看了以后头脑当中产生很多困惑,在此写出来。还请各位老师点播。

我看后感觉类就是一个自定义函数包。例如自己定义一个函数包的名称叫 A,包里面包含了好几个方法(也就是函数) sin,cos,子类 B 里面包含了方法(函数) tan 。tan 的定义使用了类 A 中的方法 sin 和 cos 。tan=A.sin/A.cos

如果这就是类。那么我们直接定义几个全局变量 sin,cos,tan 。以后直接在程序中调用不就可以了?何必非要打包到 A 和 B 里面。以后再通过 A.sin A.cos 的方式调用呢?没有本质区别啊。看起来只是形式上不同。

不过反过来一想,我上面的理解肯定有有问题的。如果类仅仅是个自定义函数包。那么就不存在某语言是否支持类或某语言不支持类的问题了。喜欢使用类的程序员。直接自己定义一个全局变量叫做 A 。然后把 A 接收的参数传给内部的函数 sin 和 cos,不一样可以实现“类”的调用??哪有这种事。如果时这样 python 哪还有必要规定专门用 class 来定义类,用 def 来定义一个方法?
所以究竟什么是类。使用类相对于使用全局变量有什么区别?有什么优势?还请老师点播。
最新回复 (75)
  • cmdOptionKana 11天前
    引用 2
    你这需要补充 “面向对象” 的基础知识,一般是 Java 书里讲这个比较透彻。但你不用学 Java,那代码不学也能看明白的,主要是理解思想。
  • hello2060 11天前
    引用 3
    这就是面向对象啊,类不仅是函数,还有成员变量。

    你想象有一个类 person, 有 firstname, last name

    有一个函数,要用到人的全名,怎么办,那这个函数里面就是 obj.getFirstName() + obj.getLastName()

    所有用到人名字的函数都有这么一个运算,

    那如果有一天这个类加了一个 midname 怎么办?所有的函数都要改?

    所以不如这个类提供了一个 getFullName(), 外部直接调用 而不需知道内部实现
  • rogwan 11天前
    引用 4
    类可以理解为上一层的封装。鸭子是类,实例就是各只鸭子;禽也可以是类,实例就是各种鸡鸭鹅;雉科也可以是类,实例就是各种鸟 /家禽都算... 以此类推,形成父类子类。如果只写一个基类“生物”,也没什么不可以。
  • hoyixi 11天前
    引用 5
    封装只是一个方面,还有继承和多态

    看你的描述,应该以前没怎么接触过编程,或者没接触过支持面向对象的编程语言。 理论终归是理论,面向对象就是一套套路,多看别人写的比较好的代码,自己多写,才能体会到底套路怎么玩的~

    编程是工程性的,工科就是要动手,光看书是没用的
  • Hstar 11天前
    引用 6
    我总结,一是为了方便代码复用,二是为了逻辑易懂,三是为了保护内部私有函数和变量,但主要就是为了方便代码复用,加快开发速度。
    至于为什么类能方便代码复用,多写写代码就知道了。
    楼上提到的继承和多态在 python 中实现不太一样,没有多态只有参数默认值,继承还有多继承。仅此方面我觉得比 java 实现要好。
  • XiaoxiaoPu 11天前
    引用 7
    面向对象是一种编程思想,并不受语言本身限制,C 语言也可以实现,例如 linux kernel 就大量的使用面向对象。只是缺少语言的原生支持,会比较繁琐,很多东西要你自己手写实现,容易出错。

    类,是编程语言对面向对象的原生支持,基于类,可以更方便的实现面向对象编程。「某语言是否支持类或某语言不支持类」其实就是语言是否**原生**支持面向对象。
  • BoarBoar 11天前
    引用 8
    你说得没错,就你这个例子来说真没多少区别
    但是如果工程更大,代码量更多呢?想象一下一个文件里定义几百个变量,是不是既混乱,又额外消耗很多内存。再想象一下一个工程几百个文件,互相引用起来得多混乱。
    面向对象正是随着代码规模的增加而出现的思想,把作用相近的变量抽象为类,本质就是为了提升代码可读性和复用性。
    重要的是这种抽象的思维,类只是一个描述思维的符号,没有类的 c 语言用 struct 一样能某种程度上实现面向对象。
  • liuzhanpeng 11天前
    引用 9
    类的出现是为了封装, 实现功能内聚; 这样就方便类与类之间的交互。以更贴合人的思维方式表现出现实中复杂的业务关系。
  • chaleaoch 11天前
    引用 10
    这里有一个封装的概念.
    命名空间的封装.
    在一个就是生命周期.

    还有就是面向对象的其他特性. 也就是面向对象思想将的那些乱七八糟的.


    都是全局变量当然没问题. 但是定义多了就有问题了.不信你上 github 上看大一点的项目源码.
  • mightofcode 11天前
    引用 11
    因为类是一个高度抽象的概念,高度抽象某种程度上就意味着不实用

    根据我的经验,思考类的本质对提高你的编程水平不会有太大帮助
  • LongMaoz 11天前
    引用 12
    封装 继承 多态 ,这是一种思想,一种概念.
    比如说你是 Human,其他人也是 Human,那么你们就同样隶属于 Human'类',
    Human 有眼口手鼻,你是通过 Human 类生成的实例,那么你也有眼口手鼻,
    更通俗的讲就是你继承了 Human 的 DNA,所以你不会有翅膀,不会有腮,不会长尾巴,
    因为这些特性是属于其他类,不属于 Human 的。
  • weizhen199 11天前
    引用 13
    透过现象看本质就是大佬看你怎么写的代码 bug 又多又 tm 难理解。
    就想了个简单容易的办法统一一下
  • Biwood 11天前
    引用 14
    不妨先抛开编程语言,就说说现实生活中的类,也叫“类别”。

    比如你叫小明,她叫小红,但是你俩属于同一个类:人类。

    “人类”这个词语在中文里本身就是比较抽象的,因为“人这个类别”只是一个概念,而不是一个在现实中可以摸得到、看得见的东西,但是你可以写下来,可以说出来,可以理解它,并跟其他人沟通。

    回到编程领域,你现在要跟机器沟通从而控制机器。机器之间的语言由 0 和 1 组成,但是人类总不能每天往电脑里输入 0 和 1 来吧,所以才有了五花八门的编程语言。而面向对象就是一种让人类更好懂的书写编程语言的思维方式,它符合人的语言结构。编程里的“类”就是现实中你写作文时用到的“类”,根本没那么复杂。
  • xuanbg 11天前
    引用 15
    类就是对象模板,嗯,是面向对象的对象。不是搞对象的对象,也不是象棋里面那一对象
  • wzzzx 11天前
    引用 16
    我个人觉得初学者没必要去搞的多清楚,你也没办法搞的很清楚的。做下去你就知道了,不管怎么解释,其实都特别的抽象
  • nutting 11天前
    引用 17
    面向对象,这是一种想模拟真实世界概念模型的做法。你不要从编程技术上考虑。
  • lxk11153 11天前
    引用 18
    https://v2ex.com/t/570873?p=1#r_7435953
  • qiumaoyuan 11天前
    引用 19
    类的核心是一组关联比较紧密的属性,然后附带上一堆操作这些属性的方法。

    当然,工作当中你会遇到各种奇葩的类的写法,比如写了一个方法内部的逻辑跟本类的属性完全无关的。只要知道这些写法是错误的,并且它们普遍存在,就连自己为了适应环境时不时也要写出这样的代码,但自己一个人做项目的时候绝对不允许并且有能力不出现这样的代码就行了。
  • qiumaoyuan 11天前
    引用 20
    我觉得许多人对面向对象理解得稀里糊涂的,主要是环境使然。看似长篇大论能讲出来一堆东西,扪心自问一下,其实根本上说服不了自己。

    不注意自己纠偏又没有对的人带,比较难走出来。
  • nutting 11天前
    引用 21
    你是初学者,做的往往都是面向过程,顺序逻辑的一些编程。软件开发可不是这么简单,比如游戏啊,图形界面啊,要构建一个现实世界的镜像的,就需要面向对象概念了
  • zsdroid 11天前
    引用 22
    A.sin A.cos 对外调用没问题啊。

    但是你的实现呢?
    sin 方法的实现写在哪??
  • charlie21 11天前
    引用 23
    搜 subtyping
    https://www.zhihu.com/question/265433666/answer/337599960
    https://medium.com/@mattia512maldini/inheritance-is-not-subtyping-b84488eca5ea

    如果我讲 OOP,我直接从 组合优于继承 开始讲,然后讲讲 traits,讲讲这篇文章,
    https://stackoverflow.com/questions/9205083/traits-vs-interfaces/9205347#9205347

    “In fact, creating traits that fulfill the capabilities required by an interface is the ideal use case.”

    再讲讲什么叫底层依赖高层,完事了
    https://www.zhihu.com/question/265433666/answer/337599960
    “一个稳定易拓展的系统应该让底层依赖高层”

    然后直接讲软件设计模式里的装饰器模式

    -
    关于 继承 inheritance
    Neither is inheritance: as a code reuse tool it is widely recognized as useless and it is slowly being discarded. Just don’t use it and no harm will come.
    -

    然后讲 IoC,用 IoC 来接管依赖关系并生成我需要的类的实例

    这样一种叙述方式可以避免 OOP 里关于父类子类这一叙述里带来的思维弯路。它是为人脑设计的障碍

    clear contracts; using interfaces will force you to think in term of communication between objects;

    thinking “communication first” will give your brain free resources by splitting its job in two steps: design time and implementation time; our brain doesn’t like to work on two different things at the same time, especially switching continuously between the two;

    https://codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117
    -
  • lychs1998 11天前
    引用 24
    面向对象的类,可以理解为“类别”这种概念。我们概括一个类别往往是提取这些东西的共性。我们以类进行分析,就是我们对这些共性进行分析。

    比如学生 ABCD,他们都有名字、学号、身份证、照片、年龄等各种属性(行为),当然个别人可能有一些个性的属性(行为),但对于学生这个身份来说这些额外的个性的东西是没有用的。

    系统里对这些共性进行总结形成了 Student 类,我们通过 Student 类去概况一个 学生 该有的属性和行为。
  • ruyu 11天前
    引用 25
    面向对象是个好东西, 但是不要被面向对象禁锢了思想.
  • nutting 11天前
    引用 26
    全局变量是面向过程的思维,类在程序运行时的体现是对象,如果你就一个对象,那和全局没啥区别。要是一个类产生多个对象呢,要互相独立的。
  • charlie21 11天前
    引用 27
    所谓的 “以后直接在程序中调用不就可以了?何必非要打包到 A 和 B 里面。以后再通过 A.sin A.cos 的方式调用呢?没有本质区别啊。看起来只是形式上不同”

    回答你的疑问:有一些时候,用函数是 OK 的没问题的;没有副作用的函数叫做纯函数。
    关于纯函数的用处,参考 纯函数为什么能消除副作用
    blog.leapoahead.com/2015/09/19/function-as-first-class-citizen/

    有一些时候,你会乐意为副作用买单:让实例去做函数的事(让纯函数不纯了)因为实例是可以实例化(初始化)的,它可以维护一些自己的状态(实例变量)。函数没有实例变量,这就是状态,数据,是实例的,不是函数的。

    在类的实例里
    num.eq(othernum); // This is like writing eq(num, othernum)
    medium.com/@mattia512maldini/inheritance-is-not-subtyping-b84488eca5ea

    最后 在写类的时候记得 组合优于继承,用 IoC,不要在父类子类上耽误时间。use composition (through constructor dependency injection 也就是让 IoC 生成类的实例 ) to put things together and prevent complexity.
    codeburst.io/inheritance-is-evil-stop-using-it-6c4f1caf5117
  • retamia 11天前
    引用 28
    去看一本叫 SICP 的书就知道了
  • zjsxwc 11天前
    引用 29
    没有,“类”就是个语法,
    如果你问“对象”有什么作用,那么楼上说的优势都是,
    但是有些语言,没有“类”,只有对象,照样还是很火,比如之前版本的 js,
    “类”仅仅只是创建“对象”的一种实现途经,但不是唯一。
  • 楼主 Hlianbobo 11天前
    引用 30
    @cmdOptionKana 谢谢回复。你认为 java 哪一本书对面向对象讲的比较透彻。是否有具体推荐?
  • 楼主 Hlianbobo 11天前
    引用 31
    @BoarBoar 你的回复,让我感觉。类,或者说面向对象。其实就是给自己写的函数间隔目录,分个类。这样以后再引用的时候,只要一看到被引用的方法是 A.b.sin 。就知道这个函数是起什么作用的了。因为你看到 A 就想到 A 这一类主要是处理什么工作的。在看到 b 更进一步明确了。类似于中粮集团大豆公司仓库修缮部刘主任。这样一看名字就知道他是做什么工作的了?是这意思么?
    那我再命名自己写的函数时按照这一思路去给全局变量函数分类。是不是就实现了面向对象的编程思想?
  • 楼主 Hlianbobo 11天前
    引用 32
    @chaleaoch 我明白全局变量定义的多了。容易出问题。请教一下。类定义以后时如何避免全局变量的风险的?是因为其命名天然就加了一个类名在变量前面么?另外,类是不是天然就可以全局调用?
  • Wirbelwind 11天前
    引用 33
    类只是抽象给写程序的人看的,底层里类展开完都是普通类型
  • 楼主 Hlianbobo 11天前
    引用 34
    @retamia 谢谢荐书。看了一下简介。是以 scheme 为例子讲解的。完全没学过这门语言。感觉读起来困难不小吧。另外做数据分析。数据挖掘对编程的技能要达到什么深度?比如面向对象的编程思想在数据挖掘领域应用普遍么?
  • dreamist 11天前
    引用 35
    em...要说面向对象的理解,还是学习 Java 好些,python 这样的动态语言,学明白挺不容易的。。
  • RoyceLee 11天前
    引用 36
    看看继承和多态就知道类的牛逼
  • cmdOptionKana 11天前
    引用 37
    @Hlianbobo 很多 Java 入门书对这个问题都讲得很不错,所以我没记下具体哪本,因为其实讲 Java 就离不开面向对象,Java 就是面向对象思想的极致表现。具体的书可以看看这两本:

    https://www.zhihu.com/question/19848946
    <p>有一本经典的书《敏捷软件开发, 原则,模式,实践》,里边详细的讲解了一个薪水支付案例,是迄今为止<b>最好的面向对象设计的例子</b></p>

    另外有个最经典的 Thinking in Java (Java 编程思想),这本书非常著名,值得一看。
  • BoarBoar 11天前
    引用 38
    @Hlianbobo 你这样搞的话就算实现 1/3 的面向对象了,也就是封装。你再想一下,如果以后业务变动,你分好类的变量要加几个进去,但是又不想也不敢动以前的老代码,与其把之前分好类的几百个变量 copy 一遍,继承一下是不是清爽得多?与其把分好类的函数 copy 改改,多态一下是不是整洁得多?
    用大白话说,封装就是把变量函数分类,继承多态就是拓展分好类的变量函数。
    另外类只是一段格式,在初始化对象时才会分配内存,比起全局变量,毫无疑问资源消耗小得多
  • wysnylc 11天前
    引用 39
    你得学个 Java 入门,就知道什么是 OOP 了
    Java 被吐槽难学冗余 OOP 有一定功劳
  • chaleaoch 11天前
    引用 40
    @Hlianbobo #31
    类定义以后时如何避免全局变量的风险的?
    类有自己的命名空间, 实例化之后,一个实例就是一个独立的命名空间. 所以将全局变量的空间进行分割啊.

    是因为其命名天然就加了一个类名在变量前面么?
    即是也不是. 我也说不清楚,参考上一个问题的回答.

    类是不是天然就可以全局调用?
    不是,参考 java 中的内部类. python 就更简单了, python 可以在类内部定义类,也可以在函数里面定义类.不要太同意.

    楼主钻牛角尖了, 我的建议是不求甚解, 快速浏览语法书之后,进项目.慢慢就懂了. 多敲代码.而不是思考.当然思考也不是坏事. 敲代码和思考需要同步进行.加油.
  • chaleaoch 11天前
    引用 41
    .不要太同意. --> 不要太简单.
  • AX5N 11天前
    引用 42
    @Hlianbobo
    “完全没学过这门语言。感觉读起来困难不小吧。另外做数据分析。数据挖掘对编程的技能要达到什么深度?”

    编程有 2 个方面,一个是编程技巧,一个是软件工程。对于你们这些并非靠开发软件来吃饭的人来说,掌握编程技巧就行,所谓的编程技巧其实也就是熟能生巧而已。你问的“类”更偏向软件工程方面,你懂不懂都问题不大,有没有类都对你没太大影响。
  • qiumaoyuan 11天前
    引用 43
    嗯…免不了无法分辨的杂音太多,各说各的。
  • ConradG 11天前
    引用 44
    “类”在语法层面上,是“命名空间”的一个不完全实现。
    而“命名空间”的语义表达一般是“在……的前提下 /范围内”。
  • namelosw 11天前
    引用 45
    其实你理解的没错,a.f(p1, p2)本质上和 f(A, p1, p2)区别没有那么大。

    但是有个区别,有了对象(不一定非得有类),你可以写出:
    def f(o):
    o.printSelf()

    那么这个 o 是什么呢? o 既不是 A,也不是 B,它是可以 printSelf()的东西。

    总得来说,几个 implication:
    1. 面向对象不一定有类
    2. 面向对象的主要效果是实现多态
    3. 抽象不一定需要类,函数就可以。而且继承往往是万恶之源,可读性和扩展性反而经常没有编写良好的函数好。即便是组合也是正确使用的函数组合性更强。
    4. 多态不一定非得面向对象,也有模式匹配之类的方式
    5. 面向对象的好处是添加新类型容易,比如有 A B 新增 C 容易,添加新的操作难,加个新方法 foo()就要把每个类型改一遍
    6. 以如下的形式使用函数会把 5 的优缺点反转过来:
    def foo(o):
    if o.type is A:
    doThis()
    if o.type is B:
    doThat()
    这样的好处是新加一个操作 bar()函数就很容易,但是新加一个类型 C 就要把所有的函数改一遍
    7. 面向对象常见的设计模式 Visitor,就是在面向对象范畴内,将 5 的优缺点反转成 6,即加操作容易,加类型难。在语言没有很好的函数支持的情况下使用 Visitor 是可以理解的,比如老版的 Java,像在 Python 或者 JavaScript 这种函数比较好用的语言里使用 Visitor 是自讨苦吃并且对设计模式没有深入理解的行为
    8. 5 和 6 的优缺点的 trade off 可以用一个叫做表达式问题(expression problem)的问题概括,典型的解决方案是 Haskell 的 type class,还有一些奇怪且难懂的解法比如 object algebra 。
    9. 还有更多的 implication 过于深入,就不展开了……

    PS:大家说的和我上面说的面向对象都是非正统面向对象,即 Simula / C++ / Java / Python 等等,即有关类+封装 /继承 /多态的面向对象。其实应该叫面向类才对。
    正统的面向对象核心是对象和消息传递,比如 Smalltalk 和 Erlang(你没看错)。
  • crella 11天前
    引用 46
    太好了,这贴劝退我学计算机,非常感谢
  • zouzou0208 11天前
    引用 47
    @namelosw 解释的真好,我记得王垠有篇文章也说这个的。
  • Jianrry 11天前
    引用 48
    所谓的类,其实就是一个概念.

    比如说你是一个人,人就是一个类,而一个个具体的人就是一个个实例.

    类由两部分组成:字段和方法.

    字段就是人的属性,比如说体重多少 kg,长多少 cm .

    方法就是人的行为,比如说吃饭,喝水等行为

    这就是类.
  • levelworm 11天前
    引用 49
    SICP 里有自己构建 oop 的内容,可以学习一下。
  • xdeng 11天前
    引用 50
    你得学会从上帝视角看就明白了。
  • ispinfx 11天前
    引用 51
    封装、继承、多态。
  • zrc 11天前
    引用 52
    狗{吃屎} 对象{方法}
    吃(狗屎) 方法(参数)
  • wanglufei 11天前
    引用 53
    以类的方式组织代码,以对象的方式组织(封装)数据
  • summerwar 11天前
    引用 54
    参考生物分类的界门纲目科属种,然后就能理解了
  • palmers 11天前
    引用 55
    就是一类实体及操作该类实体的抽象 这么说好像还是比较抽象
  • Pzqqt 11天前
    引用 56
    曾经我也沉迷于函数式编程无法自拔 看了好多资料都没法完全理解面向对象的编程思想
    直到有一天在网上受到高人指点 看到这么一段代码 瞬间茅塞顿开
    ![]( https://www.png8.com/imgs/2020/10/55857f64f52a9d83.jpg)
  • someonedeng 11天前
    引用 57
    @namelosw "正统的面向对象核心是对象和消息传递" "大家说的和我上面说的面向对象都是非正统面向对象" 好像见过这两派人在哪吵起来
  • shyrock 11天前
    引用 58
    全局变量你也可以理解为是在‘全局’这个类里面定义的成员变量,全局这个类代表了当前宇宙最顶级的类。
  • chenyu8674 11天前
    引用 59
    编程的本质是在机器能理解的东西和人能理解的东西之间做翻译
  • hhhsuan 11天前
    引用 60
    确实可以不用面向对象,一味鼓吹面向对象是一种迷信
  • ID2333 11天前
    引用 61
    封装只是一部分,而楼说的也知封装最基本的,现象都没看全,怎么看本质。。标题太大。
  • sudoy 11天前
    引用 62
    可能更多是为了方便重复利用吧,比如特别是可拓展性和可维护性更高。我刚学的时候不大习惯用类,后面看到别人都用就慢慢开始适应了
  • oNuGrInDiNg 11天前
    引用 63
    就是根据工程实践中的经验,总结出来的可复用的一些设计模式。
  • laqow 11天前
    引用 64
    写写别的语言,写点 R 感受全是全局变量的世界,写点 javascript 感受全是闭包和对象的世界
  • namelosw 11天前
    引用 65
    @someonedeng 面向对象这个词是 Alan Kay coin 的. 但是他指的是消息传递的那面向对象,跟封装继承多态没关系. 后多年面向对象火了之后就大家一直 cue 他说他发明了面向对象,他就很郁闷:我 TM 说的面向对象根本就不是这些渣渣...

    我记得 OOPSLA 97 年有个 Alan Kay 的 talk 里面提到了,可以在 Youtube 搜到.
  • q447643445 11天前
    引用 66
    类不类 方法不方法的 都是在模拟现实世界.. java 编程思想里面写的很清楚了
  • julyclyde 11天前
    引用 67
    这个理解基本是正确的
    古代的 C++就是用 struct 来做 class 的
  • 1024com 11天前
    引用 68
    类是大自然的进化法则
  • exploreXin 11天前
    引用 69
    上网搜"范畴论",哲学里面的一个分支,这是面向对象的底层理论基础。
  • way2explore2 11天前
    引用 70
    @hello2060 这歌例子也是一个开闭原则的实现
  • way2explore2 11天前
    引用 71
    好了,看了这么多,请把上边别人说的都忘掉。

    跟我念,

    everything is an object, this is OOP.
  • tairan2006 11天前
    引用 72
    楼主你 OOP 基础都没有瞎想啥=_=

    楼上还有人说 python 没多态的,简直醉了
  • wangkun025 11天前
    引用 73
    看完我竟然不知道怎么回答。
  • ydpro 11天前
    引用 74
    类是抽象,抽象出通用的,将可变的暴露出来。实现最少的,最低耦合的代码来写出复杂的功能。
  • lovecy 11天前
    引用 75
    学不懂类这个概念,面向对象还可以,类就算了,理解不了,可能 JS 写多了吧。
  • walpurgis 11天前
    引用 76
    类就是变量类型,定义类的本质就是定义变量类型,初学是我就这么理解的,别被复杂概念绕晕
    比如 py 里 s = 'abc',然后输入 type(s),就能知道 <class 'str'>,s 是 str 类型,str 是 py 内建的类
    现在我想得到 'abc' 的大写,只要调用 s.upper()就可以,很方便
    因为事先知道了 s 是 str 类型,函数 upper 就是 str 类的方法,只有 str 类型的变量才能调用 upper,相当于 str 类型的变量自带操作自己的函数
    主楼里提到 A.sin A.cos 没意义是对的,因为你的 A 里面没有存储变量,sin cos 都纯函数,没有必要成为方法,一个有意义的方法一定是跟自身所存储的数据有关系
  • 游客
    77
返回