Python面向对象编程
来源:互联网 发布:日式rpg 知乎 编辑:程序博客网 时间:2024/06/07 09:29
今天我换了一种方式学python。先看代码再分析知识点,感觉这样要快一点。(现在学习进展这么慢什么时候才能做出个东西来啊我想去的公司千万不要招满人啊我还要学驾照啊好了发泄完毕继续愉快地学python)
依旧是慕课网的例子:
请练习定义Person类,并创建出两个实例,打印实例,再比较两个实例是否相等。
要打印实例,直接使用print语句;
要比较两个实例是否相等,用==操作符。
参考代码:
class Person(object): passxiaoming = Person()xiaohong = Person()print xiaomingprint xiaohongprint xiaoming == xiaohong
请创建包含两个 Person 类的实例的 list,并给两个实例的 name 赋值,然后按照 name 进行排序。
sorted() 是高阶函数,接受一个比较函数。
参考代码:
class Person(object): passp1 = Person()p1.name = 'Bart'p2 = Person()p2.name = 'Adam'p3 = Person()p3.name = 'Lisa'L1 = [p1, p2, p3]L2 = sorted(L1, lambda p1, p2: cmp(p1.name, p2.name))print L2[0].nameprint L2[1].nameprint L2[2].name
lambda p1, p2: cmp(p1.name, p2.name)思考:此为匿名函数,作用为p1.name,p2.name,从小到大排序
sorted(L1, lambda p1, p2: cmp(p1.name, p2.name))思考:sorted()作为高阶函数,把上述匿名函数作为参数。
请定义Person类的__init__方法,除了接受 name、gender 和 birth 外,还可接受任意关键字参数,并把他们都作为属性赋值给实例。
要定义关键字参数,使用 **kw;
除了可以直接使用self.name = 'xxx'设置一个属性外,还可以通过 setattr(self, 'name', 'xxx') 设置属性。
参考代码:
class Person(object): def __init__(self, name, gender, birth, **kw): self.name = name self.gender = gender self.birth = birth for k, v in kw.iteritems(): setattr(self, k, v)xiaoming = Person('Xiao Ming', 'Male', '1990-1-1', job='Student')print xiaoming.nameprint xiaoming.job
**kw这是个字典列表,参数以k=v的形式传入,传入的个数任意,解释器会自行识别。同样,*args表示传入的参数可变。相当于c语言中printf所运用到的可变参数列表。
for k, v in kw.iteritems(): setattr(self, k, v)此代码块在用到**kw时必须写入__init__()
请给Person类的__init__方法中添加name和score参数,并把score绑定到__score属性上,看看外部是否能访问到。
以双下划线开头的属性无法被外部访问,"__xxx__"除外。
参考代码:
class Person(object): def __init__(self, name, score): self.name = name self.__score = scorep = Person('Bob', 59)print p.nametry: print p.__scoreexcept AttributeError: print 'attributeError'
如果一个属性以"__xxx__"的形式定义,那它又可以被外部访问了,以"__xxx__"定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用"__xxx__"定义。以单下划线开头的属性"_xxx"虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。
思考:__xx相当于java中的private修饰的属性,try...except...相当于try...catch..的用法
请给 Person 类添加一个类属性 count,每创建一个实例,count 属性就加 1,这样就可以统计出一共创建了多少个 Person 的实例。
由于创建实例必定会调用__init__()方法,所以在这里修改类属性 count 很合适。
参考代码:
class Person(object): count = 0 def __init__(self, name): Person.count = Person.count + 1 self.name = namep1 = Person('Bob')print Person.count# => 1p2 = Person('Alice')print Person.count# => 2p3 = Person('Tim')print Person.count# => 3
绑定在一个实例上的属性不会影响其他实例,但是,类本身也是一个对象,如果在类上绑定一个属性,则所有实例都可以访问类的属性,并且,所有实例访问的类属性都是同一个!也就是说,实例属性每个实例各自拥有,互相独立,而类属性有且只有一份。因为类属性是直接绑定在类上的,所以,访问类属性不需要创建实例,就可以直接访问。对一个实例调用类的属性也是可以访问的,所有实例都可以访问到它所属的类的属性。因为类属性只有一份,所以,当Person类的address改变时,所有实例访问到的类属性都改变了。修改类属性会导致所有实例访问到的类属性全部都受影响,但是,如果在实例变量上修改类属性会发生什么问题呢?
class Person(object): address = 'Earth' def __init__(self, name): self.name = namep1 = Person('Bob')p2 = Person('Alice')print 'Person.address = ' + Person.addressp1.address = 'China'print 'p1.address = ' + p1.addressprint 'Person.address = ' + Person.addressprint 'p2.address = ' + p2.address
结果如下:
Person.address = Earthp1.address = ChinaPerson.address = Earthp2.address = Earth
我们发现,在设置了 p1.address = 'China' 后,p1访问 address 确实变成了 'China',但是,Person.address和p2.address仍然是'Earch',怎么回事?
原因是 p1.address = 'China'并没有改变 Person 的 address,而是给 p1这个实例绑定了实例属性address ,对p1来说,它有一个实例属性address(值是'China'),而它所属的类Person也有一个类属性address,所以:
访问 p1.address 时,优先查找实例属性,返回'China'。
访问 p2.address 时,p2没有实例属性address,但是有类属性a
ddress,因此返回'Earth'。
可见,当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问。
当我们把 p1 的 address 实例属性删除后,访问 p1.address 就又返回类属性的值 'Earth'了:
del p1.addressprint p1.address# => Earth
可见,千万不要在实例上修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个实例属性。
请给 Person 类增加一个私有属性 __score,表示分数,再增加一个实例方法 get_grade(),能根据 __score 的值分别返回 A-优秀, B-及格, C-不及格三档。
注意get_grade()是实例方法,第一个参数为self。
参考代码:
class Person(object): def __init__(self, name, score): self.__name = name self.__score = score def get_grade(self): if self.__score >= 80: return 'A' if self.__score >= 60: return 'B' return 'C'p1 = Person('Bob', 90)p2 = Person('Alice', 65)p3 = Person('Tim', 48)print p1.get_grade()print p2.get_grade()print p3.get_grade()
由于属性可以是普通的值对象,如 str,int 等,也可以是方法,还可以是函数,大家看看下面代码的运行结果,请想一想 p1.get_grade 为什么是函数而不是方法:
class Person(object): def __init__(self, name, score): self.name = name self.score = score self.get_grade = lambda: 'A'p1 = Person('Bob', 90)print p1.get_gradeprint p1.get_grade()
p1.get_grade 返回的是一个函数对象,但这个函数是一个绑定到实例的函数,p1.get_grade() 才是方法调用。因为方法也是一个属性,所以,它也可以动态地添加到实例上。
思考:方法和对象相关;函数和对象无关。Java中只有方法,C中只有函数,而C++里取决于是否在类中。不理解方法为什么也是属性,在构造方法里写方法也从来没见过。python特有的。如果将类属性 count 改为私有属性__count,则外部无法读取__count,但可以通过一个类方法获取,请编写类方法获得__count值。
注意类方法需要添加 @classmethod
参考代码:
class Person(object): __count = 0 @classmethod def how_many(cls): return cls.__count def __init__(self, name): self.name = name Person.__count = Person.__count + 1print Person.how_many()p1 = Person('Bob')print Person.how_many()
请参考 Student 类,编写一个 Teacher类,也继承自 Person。
要正确调用 super() 的__init__方法。
参考代码:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = genderclass Teacher(Person): def __init__(self, name, gender, course): super(Teacher, self).__init__(name, gender) self.course = courset = Teacher('Alice', 'Female', 'English')print t.nameprint t.course
思考:继承一个类,是把类像传参一样写在括号里。
请根据继承链的类型转换,依次思考 t 是否是 Person,Student,Teacher,object 类型,并使用isinstance()判断来验证您的答案。
注意t 是Teacher的实例,继承链是:
object <- Person <- Teacher
参考代码:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = genderclass Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = scoreclass Teacher(Person): def __init__(self, name, gender, course): super(Teacher, self).__init__(name, gender) self.course = course
t = Teacher('Alice', 'Female', 'English')print isinstance(t, Person)print isinstance(t, Student)print isinstance(t, Teacher)print isinstance(t, object)
类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个 whoAmI() 方法:
class Person(object): def __init__(self, name, gender): self.name = name self.gender = gender def whoAmI(self): return 'I am a Person, my name is %s' % self.nameclass Student(Person): def __init__(self, name, gender, score): super(Student, self).__init__(name, gender) self.score = score def whoAmI(self): return 'I am a Student, my name is %s' % self.nameclass Teacher(Person): def __init__(self, name, gender, course): super(Teacher, self).__init__(name, gender) self.course = course def whoAmI(self): return 'I am a Teacher, my name is %s' % self.name
在一个函数中,如果我们接收一个变量 x,则无论该 x 是 Person、Student还是 Teacher,都可以正确打印出结果:
def who_am_i(x): print x.whoAmI()p = Person('Tim', 'Male')s = Student('Bob', 'Male', 88)t = Teacher('Alice', 'Female', 'English')who_am_i(p)who_am_i(s)who_am_i(t)
运行结果:
I am a Person, my name is TimI am a Student, my name is BobI am a Teacher, my name is Alice
这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s是Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
由于Python是动态语言,所以,传递给函数 who_am_i(x)的参数 x 不一定是 Person 或 Person 的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可:
class Book(object): def whoAmI(self): return 'I am a book'
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。
多重继承的继承链就不是一棵树了,它像这样:
class A(object): def __init__(self, a): print 'init A...' self.a = aclass B(A): def __init__(self, a): super(B, self).__init__(a) print 'init B...'class C(A): def __init__(self, a): super(C, self).__init__(a) print 'init C...'class D(B, C): def __init__(self, a): super(D, self).__init__(a) print 'init D...'
总结:总的来说,python在面向对象这部分和c++以及java类似,我目前比较熟悉java,所以会不由自主地拿java来对比。java比c++更简洁,python比java更简洁。现在我才深深地体会到,语言只是工具,重要的是编程思想。面向对象的思想,无论哪种语言,都是异曲同工的,后面学习网络编程的时候,可能也会如此。所以我现在不再关注哪种编程语言容易就业了,专心改变思维方式,打好数据结构和算法的基础,精通一种语言就够了。但是c++号称永远无法精通,所以我选择了python。
- python面向对象编程
- python 面向对象编程
- python面向对象编程
- python面向对象编程
- Python 面向对象编程
- Python面向对象编程
- Python 面向对象编程
- python--面向对象编程
- python面向对象编程
- python 面向对象编程
- Python:面向对象编程
- python面向对象编程
- Python面向对象编程
- python面向对象编程
- Python面向对象编程
- python 面向对象编程
- python 面向对象编程
- PYTHON 面向对象编程
- WCF服务:HTTP 无法注册 URL http://+:8000 进程不具有此命名空间的访问权限
- 匿名函数
- Android重拾设计模式系列——工厂方法模式
- App启动优化最佳实践
- 实习生的感悟,最近不踏实
- Python面向对象编程
- 架构 - 第四课:git分支管理
- 对匿名函数的深入理解(彻底版)
- DOM4J解析XML文档报错document root element is missing
- 关于统计变换(CT/MCT/RMCT)算法的学习和实现,也称为局部二进制模式(LBP)
- C++读取txt文件获取txt文件行数
- lucene源码分析---13
- 第十二周项目3--图遍历算法实现--实现广度优先遍历——BFS
- 对javascript匿名函数的理解(透彻版)