Python学习笔记-面向对象

wiki上说Py的启发语言有C/C++、java等,果然语言都是想通的,定义形式上差不多~~
不过后面的一些特性确实厉害,不过….就是看不太懂 = =,要达到熟练使用的地步…(摊手)

我感觉出了:将Py比作是坦克很形象!

类和实例

在Python中,定义类也是通过class关键字,类名通常是大写开头的单词,经典的student例子:

1
2
class Student(object):
pass

括号中的object是表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
然后就是创建实例,Py中不需要new关键字:

1
2
3
4
5
6
7
8
9
10
>>> bart = Student()
>>> bart
<__main__.Student object at 0x10a67a590>
>>> Student
<class '__main__.Student'>

# 绑定属性
>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'

还可以自定义初始化:

1
2
3
4
5
6
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))

注意到__init__方法的第一个参数永远是self表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去,总的来说挺想构造函数的
注意:相同名称的实例属性将屏蔽掉类属性

访问限制

在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量

有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过xxx._Student__name来访问__name变量,但是强烈建议你不要这么干,因为不同版本的Python解释器可能会把__name改成不同的变量名。
如果你确实使用了bart.__name = xxx这样的语句,确实不会报错,它真的会给bart增加一个__name 属性,但它不是内部的name变量,内部的name变量名叫_xxx__name

动态语言和静态语言中的多态

对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个可以调用的方法就可以了

动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

进阶-slots相关

给实例绑定属性很简单,直接点就行了,还可以给实例绑定方法,当然这些都是在这一个实例中有效而已

1
2
3
4
5
6
7
8
>>> def set_age(self, age): # 定义一个函数作为实例方法
... self.age = age
...
>>> from types import MethodType
>>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
>>> s.set_age(25) # 调用实例方法
>>> s.age # 测试结果
25

但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加nameage属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

1
2
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

进阶-使用@property

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,如果使用get/set的方法实现感觉又太麻烦,记得前面学过装饰器,Python内置的@property装饰器就是负责把一个方法变成属性
@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student(object):
@property
def score(self):
return self._score

@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!') # 抛出异常
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value

>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60

进阶-多继承与定制类

对,是的,Py是支持多继承的,用逗号分开即可,多继承的这种设计通常称之为MixIn。
我们知道以双下划线开头的都是特殊的,比如

  • __str__()返回用户看到的字符串,__repr__()返回程序开发者看到的字符串
  • 如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
  • 如果要像list那样按照下标取出元素,就需要实现__getitem__()方法
  • 当调用不存在的属性时(也就是找不到的时候),Python解释器会试图调用__getattr__(self, 'XXX')来尝试获得属性,这个方法也是可以返回函数的,不过调用的时候要加()执行
  • 任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。(name())
    通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

进阶-枚举类与元类

貌似3.5版本以后才支持呢,定义一个枚举类类似:

1
2
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,上面的定义等价于:

1
2
3
4
5
class Month(Enum):
Jan = 1
Feb = 2
Mar = 3
Apr = 4

当然还有些高级用法,我没怎么看,因为感觉智商有点不够用

Py作为解释性语言,在执行的时候才会编译,所以动态生成类就有了可能,type()函数可以查看一个类型或变量的类型,同时也可以产生新的类,比如:

1
2
3
4
>>> def fn(self, name='world'): # 先定义函数
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class

要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000

喜欢就请我吃包辣条吧!

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~