Crossin的编程教室

标题: 【Python 第49课】 面向对象(3) [打印本页]

作者: crossin先生    时间: 2013-8-9 20:25
标题: 【Python 第49课】 面向对象(3)
面向对象是比较复杂的概念,初学很难理解。我曾经对人夸张地说,面向对象是颠覆你编程三观的东西,得花上不少时间才能搞清楚。我自己当年初学Java的时候,也是折腾了很久才理清点头绪。所以我在前面的课程中没有去提及类和对象这些概念,不想在一开始给大家造成混淆。
在刚开始编程的时候,从上到下一行行执行的简单程序容易被理解,即使加上if、while、for之类的语句以及函数调用,也还是不算困难。有了面向对象之后,程序的执行路径就变得复杂,很容易让人混乱。不过当你熟悉之后会发现,面向对象是比面向过程更合理的程序设计方式。
今天我用一个例子来展示两种程序设计方式的不同。
假设我们有一辆汽车,我们知道它的速度(60km/h),以及A、B两地的距离(100km)。要算出开着这辆车从A地到B地花费的时间。(很像小学数学题是吧?)
面向过程的方法:
  1. speed = 60.0
  2. distance = 100.0
  3. time = distance / speed
  4. print time
复制代码
面向对象的方法:
  1. class Car:
  2.     speed = 0
  3.     def drive(self, distance):
  4.         time = distance / self.speed
  5.         print time

  6. car = Car()
  7. car.speed = 60.0
  8. car.drive(100.0)
复制代码
看上去似乎面向对象没有比面向过程更简单,反而写了更多行代码。
但是,如果我们让题目再稍稍复杂一点。假设我们又有了一辆更好的跑车,它的速度是150km/h,然后我们除了想从A到B,还要从B到C(距离200km)。要求分别知道这两种车在这两段路上需要多少时间。
面向过程的方法:
  1. speed1 = 60.0
  2. distance1 = 100.0
  3. time1 = distance1 / speed1
  4. print time1

  5. distance2 = 200.0
  6. time2 = distance2 / speed1
  7. print time2

  8. speed2 = 150.0
  9. time3 = distance1 / speed2
  10. print time3

  11. time4 = distance2 / speed2
  12. print time4
复制代码
面向对象的方法:
  1. class Car:
  2.     speed = 0
  3.     def drive(self, distance):
  4.         time = distance / self.speed
  5.         print time

  6. car1 = Car()
  7. car1.speed = 60.0
  8. car1.drive(100.0)
  9. car1.drive(200.0)

  10. car2 = Car()
  11. car2.speed = 150.0
  12. car2.drive(100.0)
  13. car2.drive(200.0)
复制代码
对比两种方法,面向过程把数据和处理数据的计算全部放在一起,当功能复杂之后,就会显得很混乱,且容易产生很多重复的代码。而面向对象,把一类数据和处理这类数据的方法封装在一个类中,让程序的结构更清晰,不同的功能之间相互独立。这样更有利于进行模块化的开发方式。

49class.png

面向对象的水还很深,我们这里只是粗略一瞥。它不再像之前print、while这些概念那么一目了然。但也没必要对此畏惧,等用多了自然就熟悉了。找一些实例亲手练练,会掌握得更快。遇到问题时,欢迎来论坛和群里讨论。
#==== Crossin的编程教室 ====#
微信ID:crossincode
论坛:http://crossin.me
QQ群:312723402

面向零基础初学者的编程课
每天5分钟,轻松学编程




作者: cyin15288    时间: 2013-8-9 22:58
难道是沙发吗!
作者: feicien    时间: 2013-8-10 10:09
为什么没有人回帖呢
作者: SH40Z1HU1    时间: 2013-8-12 00:19
当初开发出面对对象编程概念的人们都好厉害 由衷佩服
作者: 文书    时间: 2013-9-4 00:05
先生,你更详细的讲解下面向对象吗?这块比较抽象 不是很明白!
作者: crossin先生    时间: 2013-9-4 14:07
文书 发表于 2013-9-4 00:05
先生,你更详细的讲解下面向对象吗?这块比较抽象 不是很明白!

去找一些面向对象的简单实例看看吧。
我也去找找,找到了分享出来。
如你所说,这块很抽象,光讲概念很难说明白。

可以结合具体例子的问题分析。
作者: 羽扇纶巾    时间: 2013-12-1 23:53
你好! 请问一下

class Car:
    speed = 0
    def drive(self, distance):
        time = distance / self.speed
        print time

这个类声明的时候,为什么只声明一个变量speed
self在这里的作用又是什么呢?
作者: crossin先生    时间: 2013-12-2 19:06
羽扇纶巾 发表于 2013-12-1 23:53
你好! 请问一下

class Car:

speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调用,self指向的就是这个实例本身
作者: 脑子有音乐    时间: 2014-6-27 16:40
crossin先生 发表于 2013-12-2 19:06
speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调 ...

speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调用,self指向的就是这个实例本身
------------------------------------------------------------------
能解释的再通俗点吗?
作者: crossin先生    时间: 2014-6-28 17:02
脑子有音乐 发表于 2014-6-27 16:40
speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调 ...

每辆车都有一个速度的属性(speed),每辆车都有一种驾驶的方法(drive)。驾驶方法里的self是用来指向这辆车本身,这样保证你驾驶的是“这辆车”,而不是其他的车
作者: 脑子有音乐    时间: 2014-6-28 21:19
class MyClass:
    count = 0
    name = "DefaultName"
    def __init__(self,name):
        self.name = name
        print "the class name is: %s\nthe self name is %s"%(MyClass.name,self.name)
    def GetInfo(self,name):
        print "get information by:%s"% (self.name)
        print "get information by:%s"% (name)

p=MyClass("Joan")
p.GetInfo("KK")
---------------------------------------------------
the class name is: DefaultName
the self name is Joan
get information by:Joan
get information by:KK
只有调用__init__对象的变量的时候要加self,其他可以不用加是吗? 这里不是特别懂
作者: crossin先生    时间: 2014-6-30 17:22
脑子有音乐 发表于 2014-6-28 21:19
class MyClass:
    count = 0
    name = "DefaultName"

申明时,所有类方法的第一个参数必须是 self,这个会指向调用这个方法的对象本身。

调用时不需要你加,指向调用者自身的引用会被自动赋值给这个self
作者: liu-pengfei    时间: 2014-9-27 00:05
看了几天的面向对象,好像有一点点懂了,以后更加努力。
作者: catherinemic    时间: 2016-1-28 23:34
的确有些抽象,看了一些别人写的文章也似懂非懂,感觉还是crossin先生这样举一些循序渐进的例子更有助于理解。
作者: 212121212    时间: 2016-3-29 09:05
self.speed 是什么鬼,self是变量啊,self.speed 这个格式很不理解
作者: crossin先生    时间: 2016-3-29 13:11
212121212 发表于 2016-3-29 09:05
self.speed 是什么鬼,self是变量啊,self.speed 这个格式很不理解

self是一个指向实例自身的变量
作者: 大肚皮的python    时间: 2016-5-31 20:37
  1. class vehicle:
  2.     def _init_(self,speed):
  3.         self.speed = speed
  4.     def drive(self,distance):
  5.         print 'need %.2f time'%float(distance)/self.speed
  6.         
  7. class Bike(vehicle):
  8.     pass
  9. class Car(vehicle):
  10.     def _init_(self,speed,fuel):
  11.         vehicle._init_(self,speed)
  12.         self.fuel = fuel
  13.     def drive(self,distace):
  14.         vehicle.drive(self,distace)
  15.         print 'need %f fuel'%(distace*self.fuel)

  16. a = Bike(15.0)
  17. b = Car(80.0,0.0025)
  18. a.drive(50)
  19. b.drive(50)
复制代码
上述代码有什么问题呢?总是显示Bike类不需要参数。。。
作者: crossin先生    时间: 2016-6-1 11:59
大肚皮的python 发表于 2016-5-31 20:37
上述代码有什么问题呢?总是显示Bike类不需要参数。。。

__init__ 是两个下划线,系统内置方法和属性都是两个下划线
作者: 大肚皮的python    时间: 2016-6-1 16:02
这个不仔细看还真不容易发现,thx!
作者: Joshtu    时间: 2016-7-14 17:02
crossin先生 发表于 2013-12-2 19:06
speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调 ...

先生:
这个Class Car里面:time = distance / speed也不会报错。
class Car:
        speed = 0
        def drive(self, distance):
                time = distance / speed
                print time
和你写的 time = distance / self.speed
效果一样吗?
作者: crossin先生    时间: 2016-7-15 00:11
Joshtu 发表于 2016-7-14 17:02
先生:
这个Class Car里面:time = distance / speed也不会报错。
class Car:

不一样。没报错是因为你没有调用 drive 方法吧
作者: Joshtu    时间: 2016-7-15 09:51
crossin先生 发表于 2016-7-15 00:11
不一样。没报错是因为你没有调用 drive 方法吧

先生,调用了drive的哦
>>> class Car:
    speed = 0
    def drive(self, distance):
        time = distance / speed
        print time

        
>>> car = Car()
>>> car.speed = 60.0
>>> car.drive(100.0)
1.66666666667
作者: Joshtu    时间: 2016-7-15 10:11
Joshtu 发表于 2016-7-15 09:51
先生,调用了drive的哦
>>> class Car:
    speed = 0

先生我明白了,加self.speed和不加self的区别了!
如果class里面不加self.speed,直接用speed;每次调用的结果都是一样的:
上梨子:
>>> car1 = Car()
>>> car1.speed = 60.0
>>> car1.drive(100.0)
1.66666666667
c
>>> car1.drive(200.0)
3.33333333333
>>> car2 = Car()
>>> car2.speed = 150.0
>>> car2.drive(100.0)
1.66666666667
>>> car2.drive(200.0)
3.33333333333
作者: crossin先生    时间: 2016-7-15 13:17
Joshtu 发表于 2016-7-15 10:11
先生我明白了,加self.speed和不加self的区别了!
如果class里面不加self.speed,直接用speed;每次调用的 ...

应该是你前面设定过一个变量叫speed。不然speed会引起为定义错误
作者: l0ve1o24    时间: 2017-1-6 10:38
老师
speed = 0
car.drive(100.0)
这两个地方不是很明白,speed后面不是可以随意变动吗?为什么要先给他赋值为0
car.drive 这里不是从类里调用函数吗
def drive(self, distance)这个函数的参数有两个,为什么一定会是distance

作者: crossin先生    时间: 2017-1-6 20:48
l0ve1o24 发表于 2017-1-6 10:38
老师
speed = 0
car.drive(100.0)

赋值为0是为了确保在使用之前有一个默认值,不然直接调用的话会报错。

类函数的第一个参数 self 是不用你传递的,指向函数的调用对象
作者: brahmagupta    时间: 2017-2-28 20:20
当初刚入学的时候,必修课学的C艹。做作业时候,感觉很多需求无法实现或者实现起来很困难。后来旁听Objective C的时候才恍然大雾:原来还有面向对象这种东西。。。。
(虽然有些云里雾里的,而且STL模板差点把我吓退了。但是 根据我当时想到的需求和疑惑,对 面向对象的方法 有了一个大概的认知)
作者: brahmagupta    时间: 2017-2-28 20:22
感觉形式上 有点类似 结构体 struct
作者: crossin先生    时间: 2017-2-28 23:57
brahmagupta 发表于 2017-2-28 20:22
感觉形式上 有点类似 结构体 struct

这两个确实有相似之处
作者: zbxdjqls    时间: 2017-8-2 19:28
把self关键字可以替换成其他,如毫无意义的DD,TT等。self只是这个参数的一个名字而已。

声明对象内容中的定义函数中有两个参数(self,distance),实际调用时因为第一个参数是指向本身不需要传递,只需传递distance。

老师我这样理解差不多吗?
作者: crossin先生    时间: 2017-8-2 23:14
zbxdjqls 发表于 2017-8-2 19:28
把self关键字可以替换成其他,如毫无意义的DD,TT等。self只是这个参数的一个名字而已。

声明对象内容中的 ...

是的
作者: 西门子不吹雪    时间: 2017-11-5 21:55
看了面对对象前两节课脑子还一团浆糊,现在看了这个例子,大致有一个表层的认识了。
作者: Selvaria    时间: 2017-11-9 17:19
class Car(object):
    def __init__(self,speed,distance):
        self.speed = speed
        self.distance = distance
        time = distance / self.speed
        print (time)

car1 = Car(60,100)
car2 = Car(150,100)
car3 = Car(150,200)
作者: wwyy4ever    时间: 2018-2-12 14:30
crossin先生 发表于 2013-12-2 19:06
speed是成员变量,这个类的每个实例都会有一个属于自己的speed
drive是成员函数,它要被这个类的实例所调 ...

为什么不能是distance=0 def drive(self,speed) time=distance/self.distance print time
作者: crossin先生    时间: 2018-2-12 22:23
wwyy4ever 发表于 2018-2-12 14:30
为什么不能是distance=0 def drive(self,speed) time=distance/self.distance print time

没明白你的问题
作者: coolqing    时间: 2018-4-25 18:29
Selvaria 发表于 2017-11-9 17:19
class Car(object):
    def __init__(self,speed,distance):
        self.speed = speed

请问老师@crossin 这位同学的做法是正解吗?我也有此疑问,直接设置两个参数,不是更省事吗,望老师帮忙解答!
作者: crossin先生    时间: 2018-4-25 23:14
coolqing 发表于 2018-4-25 18:29
请问老师@crossin 这位同学的做法是正解吗?我也有此疑问,直接设置两个参数,不是更省事吗,望老师帮忙 ...

distance不是汽车的属性
你想啊,每次汽车开的路不是固定的,这样岂不是一辆车只能走同一段路了
(虽然严格来说,speed也不是固定的,但意思你可以理解下)
作者: coolqing    时间: 2018-4-26 10:46
crossin先生 发表于 2018-4-25 23:14
distance不是汽车的属性
你想啊,每次汽车开的路不是固定的,这样岂不是一辆车只能走同一段路了
(虽然严 ...

还是不太理解,我的意思是说在函数定义时直接定义speed和distance两个参数,这样之后调用函数赋值的时候,不是会更方便些吗?

class Car(object):
    def __init__(self,speed,distance):
        self.speed = speed
        self.distance = distance
        time = distance / self.speed
        print (time)

car1 = Car(60,100)
car2 = Car(150,100)
car3 = Car(150,200)
作者: crossin先生    时间: 2018-4-26 13:58
coolqing 发表于 2018-4-26 10:46
还是不太理解,我的意思是说在函数定义时直接定义speed和distance两个参数,这样之后调用函数赋值的时候 ...

你这不叫调用函数,你每次都创建了一个新的对象(新的一辆车),而不是一辆车去行驶不同的距离
作者: delapi    时间: 2019-10-5 05:42
请问加一位小数点是有什么约定俗成的惯例吗?还是只是个人习惯?
作者: crossin先生    时间: 2019-10-5 11:36
delapi 发表于 2019-10-5 05:42
请问加一位小数点是有什么约定俗成的惯例吗?还是只是个人习惯?

加了是float,不加是int
作者: delapi    时间: 2019-10-5 21:13
crossin先生 发表于 2019-10-5 11:36
加了是float,不加是int

其实我想问的就是用float而不用int的原因。当然这个例子两者是没什么可察觉的区别的。但是不是在更复杂的情况下,这种做法就比用int有明显的好处了?
作者: crossin先生    时间: 2019-10-6 16:09
delapi 发表于 2019-10-5 21:13
其实我想问的就是用float而不用int的原因。当然这个例子两者是没什么可察觉的区别的。但是不是在更复杂的 ...

就这题来说没差别
不必太深究
作者: 宫城良田的女友    时间: 2020-6-12 14:49
想请问crossin先生:
def  drive (self , distance) 里,参数是 [self , distance]还是 [self] 和 [distance]呢,不是很明白。
另外
Time = distance / self.speed 的 self.speed是在调用类变量吗 (对象.变量名)那么这里的self就是 对象了?

感觉有点乱,望先生指点。
作者: crossin先生    时间: 2020-6-12 16:16
宫城良田的女友 发表于 2020-6-12 14:49
想请问crossin先生:
def  drive (self , distance) 里,参数是 [self , distance]还是 [self] 和 [distanc ...

1. 两个都是参数。只是self不用自己传递,是对象本身
2. self.speed 是 对象的变量




欢迎光临 Crossin的编程教室 (https://bbs.crossincode.com/) Powered by Discuz! X2.5