wiki上说Py的启发语言有C/C++、java等,果然语言都是想通的,定义形式上差不多~~
不过后面的一些特性确实厉害,不过….就是看不太懂 = =,要达到熟练使用的地步…(摊手)
我感觉出了:将Py比作是坦克很形象!
类和实例
在Python中,定义类也是通过class
关键字,类名通常是大写开头的单词,经典的student例子:
1 | class Student(object): |
括号中的object是表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
然后就是创建实例,Py中不需要new关键字:
1 | bart = Student() |
还可以自定义初始化:
1 | class Student(object): |
注意到__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 | def set_age(self, age): # 定义一个函数作为实例方法 |
但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name
和age
属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__
变量,来限制该class实例能添加的属性:
1 | class Student(object): |
使用__slots__
要注意,__slots__
定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
进阶-使用@property
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,如果使用get/set的方法实现感觉又太麻烦,记得前面学过装饰器,Python内置的@property
装饰器就是负责把一个方法变成属性@property
本身又创建了另一个装饰器@score.setter
,负责把一个setter方法变成属性赋值
1 | class Student(object): |
进阶-多继承与定制类
对,是的,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 | from enum import Enum |
这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan
来引用一个常量,上面的定义等价于:
1 | class Month(Enum): |
当然还有些高级用法,我没怎么看,因为感觉智商有点不够用
Py作为解释性语言,在执行的时候才会编译,所以动态生成类就有了可能,type()
函数可以查看一个类型或变量的类型,同时也可以产生新的类,比如:
1 | def fn(self, name='world'): # 先定义函数 |
要创建一个class对象,type()
函数依次传入3个参数:
- class的名称;
- 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
- class的方法名称与函数绑定,这里我们把函数
fn
绑定到方法名hello
上。
通过type()
函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()
函数创建出class。
除了使用
type()
动态创建类以外,要控制类的创建行为,还可以使用metaclass。
metaclass,直译为元类,简单的解释就是:
当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。
但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。
连接起来就是:先定义metaclass,就可以创建类,最后创建实例。所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~