Python学习笔记——面向对象编程
Posted _tsubasa_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python学习笔记——面向对象编程相关的知识,希望对你有一定的参考价值。
接下来学习面向对象编程,基础的就不记录了,只记录一些Python特有的或者高级的特性。
1. 类的定义
定义类使用class关键字,后面紧跟类名(首字母大写),接着是从哪个类继承下来的(所有类最终会继承object)。
通过类名加参数就可以创建实例。可以自由的给实例绑定属性。可以把一些必须绑定的属性写在类中,通过__init__方法在初始化时将属性值进行绑定。
类中的函数与普通函数的区别是第一个参数是self,表示创建的实例本身(在调用时不需要传,Python解释器自己会把实例变量传进去)。
class Person(object): def __init__(self, name, age): self.name = name self.age = age def say_hello(self): print("Hello, my name is %s and I'm %s years old" % (self.name, self.age)) tom = Person('tom', 20) tom.say_hello() # Hello, my name is tom and I'm 20 years old
2. 访问限制
如果要让类的内部属性不被外部访问,可以在前面加上两个_,就变成了私有变量。
如果外部要访问/修改变量,可以加上get_xxx、set_xxx这样的方法。
前后都有两个_的是特殊变量,可以直接访问。
开头有一个_的变量是“约定俗成”的私有变量,外部可以访问,但是不要这么做。
开头有两个_的变量其实也是可以访问的,假设类名是Person,变量名是__name,在目前版本的Python中,外部可以通过_Person__name来访问__name变量。但是不建议这么做,除了规范外,不同版本的Python解释器会把__name改成不同的变量名。
3. 继承和多态
object是根(基类),任何类都可以追溯到object。
多态的含义(假设有一个类Animal,里面有一个run方法):
对于一个变量,我们只需要知道它是Animal类型,无需确切知道它的子类型,就可以调用run()方法,而具体调用的run()方法是作用在Animal、Dog还是Cat对象上,由运行时刻该对象的确切类型决定。即调用方只管调用,不管细节,当新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是开闭原则:
对扩展开放:允许新增Animal的子类;
对修改封闭:不需要修改依赖Animal类型的函数。
对于静态语言(如Java),如果需要传入Animal类型,则传入的对象必须是Animal类型或它的子类;而对于Python这种动态语言,不一定需要传入Animal类型,只需要保证传入的对象有一个run()方法就可以了。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
Python支持多重继承,常使用MixIn的设计模式。举个例子,Python自带了TCPServer和UDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn和ThreadingMixIn提供。通过组合,我们就可以创造出合适的服务来。
4. 获取对象信息
type()函数可以获取对象的类型。
对于有继承关系的class来说,可以使用isinstance()函数。该函数判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。
ininstance()还支持判断一个变量是否是某些类型中的一种:
isinstance([1, 2, 3], (list, tuple))
dir()用于获取一个对象的所有属性和方法,它返回一个包含字符串的list。
方法也是属性,如果想知道哪些是方法,可以再使用callable()进行判断。
getattr()、setattr()和hasattr()用于操作对象的属性和函数,如:
# obj对象是否有属性x hasattr(obj, 'x') # 设置一个属性y setattr(obj, 'y', 20) # 获取属性y getattr(obj, 'y') # 获取属性z(如果试图获取不存在的属性,会有错误,因此可以给一个默认值) getattr(obj, 'z', 404)
5. 实例属性和类属性
给实例绑定属性的方法是通过实例变量,或者通过self变量。
而如果想给类本身绑定一个属性,可以直接在class中定义属性。
类属性实例也可以使用,不过前提是实例没有这个属性。
个人认为比较好的方式是实例属性和类属性不要使用相同的名字,另外在获取类属性的时候,通过类名来获取,而不是通过实例名来获取。
6. 使用@propery
@property装饰器可以将一个方法变为属性调用。
把一个getter方法变成属性赋值,只需要加上一个@property就可以了。这时@property本身又创建了另一个装饰器@xxx.setter,其中xxx是属性名,负责把一个setter方法变成属性赋值。
还可以只定义getter方法,这样这个属性就是一个只读属性。
class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): return 2016 - self._birth s = Student() s.birth = 1990 s.birth s.age
7. 定制类
Python的class中有许多形如__xxx__这样有特殊用途的函数,可以帮助我们定制类。
__len__
定义了__len__()方法的class,可以直接作用于len()函数,即len()函数内部调用了对象的__len__()方法。
__slots__
我们可以给实例绑定一个属性,也可以给实例绑定一个方法:
def set_age(self, age): self.age = age from types import MethodType s.set_age = MethodType(set_age, s) # 其中s为某类的实例
这样绑定的方法只能是当前实例有效,如果想让所有实例都有效,可以给类绑定方法:
Person.set_age = set_age
但是,如果我们想限制实例的属性怎么办?可以在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class Student(object): __slots__ = ('name', 'age')
__slots__定义的属性仅对当前类实例起作用,对类不起作用,对继承的子类的实例也不起作用。除非在子类中也定义__slots__,这样子类允许定义的属性就是自身的__slots__再加上父类的__slots__。
class Person(object): def __str__(self): return 'str' def __repr__(self): return 'repr' # __repr__ = __str__ p = Person() print(p) # str p # repr
class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b if self.a > 10: raise StopIteration() return self.a for n in Fib(): print(n) # 1 # 1 # 2 # 3 # 5 # 8
def Chain(object): def __init__(self, path = ''): self._path = path def __getattr__(self, path): return Chain("%s/%s" % (self._path, path)) def __str__(self): return self._path __repr__ = __str__ Chain().status.user.timeline.list # /status/user/timeline/list
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar')) for name, member in Month.__members__.items(): print(name, '->', member, ',', member.value) # Jan -> Month.Jan , 1 # Feb -> Month.Feb , 2 # Mar -> Month.Mar , 3枚举值默认是从1开始的。如果要自定义值,可以从Enum派生出自定义类:
from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 Mon = 1 Tue = 2 day1 = Weekday.Mon print(day1) # Weekday.Mon print(Weekday.Mon) # Weekday.Mon print(Weekday['Mon'] # Weekday.Mon print(Weekday(1)) # Weekday.Mon print(day1 == Weekday.Mon) # True
def fn(self, name = 'world'): pass Hello = type('Hello', (object,), dict(hello = fn)) h = Hello() h.hello() print(type(Hello)) # <class 'type'> print(type(h)) # <class '__main__.Hello'>
以上是关于Python学习笔记——面向对象编程的主要内容,如果未能解决你的问题,请参考以下文章