python 面向对象编程
所有方法的第一个参数必须是 self,self 表示类是实例本身,类似于 c++ 中的 this 指针。当然我们也可以讲 self 写成其它名字:
1 class MyClass(object): 2 i = 123 3 def f(this):#self写成this也是正确的 4 return 'hello' 5 6 gel = MyClass() 7 print(gel.i) 8 print(gel.f())
构造函数
行为基本和 c++ 中的构造函数类似,但当有多个构造函数时,只能调用最后一个构造函数,否则会报错:
1 #!/usr/bin/python3 2 3 class gel(object): 4 def __init__(self): 5 print("构造函数1") 6 def __init__(self, cnt): 7 print(cnt) 8 9 # a = gel()#如果定义了多个构造函数,则只能调用最后一个,否则会报错 10 a = gel(1)
注意:一个类中可以定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化。因此通常一个类中只定义一个构造函数
类的访问权限
在 python 中,类成员变量名如果以 __ 开头,就会编程私有变或私有方法,只能在类内部访问,外部不能访问
继承
在继承中,基类的构造方法(__init__() 方法)不会被自动调用,需要在子类的构造方法中专门调用
在调用基类的方法时需要加上基类的类名前缀,并带上 self 参数变量。区别于在类中调用普通函数时不需要带 self 参数
在 python 中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找
代码:
1 #!/usr/bin/python3 2 3 class Animal(object): 4 def run(self): 5 print('Animal is running...') 6 7 class Dog(Animal): 8 pass 9 10 class Cat(Animal): 11 pass 12 13 dog = Dog() 14 dog.run() 15 16 cat = Cat() 17 cat.run() 18 19 # 输出: 20 # Animal is running... 21 # Animal is running...
注意:派生类中也不能调用基类的私有方法
多态
python 的多态不同于 c++ 中需要通过虚函数来实现。当子类和父类存在相同的方法时,运行代码时总是自动调用对应对象的版本:
1 #!/usr/bin/python3 2 3 class Animal(object): 4 def run(self): 5 # pass 6 print('Animal is running...') 7 8 class Dog(Animal): 9 # pass 10 def run(self): 11 print('Dog is running...') 12 13 class Cat(Animal): 14 # pass 15 def run(self): 16 print('Cat is running...') 17 18 dog = Dog() 19 dog.run() 20 21 cat = Cat() 22 cat.run() 23 24 animal = Animal() 25 animal.run() 26 27 # 输出: 28 # Dog is running... 29 # Cat is running... 30 # Animal is running...
多重继承
多重继承的类定义:
class DerivedClassName(Base1, Base2, Base3):
pass
需要注意圆括号中父类的顺序,若父类中有相同的方法名,在子类使用时未指定,python 会从左到有搜索。
调用对应方法时,若方法在子类中未找到,则从左到右查找父类中是否包含该方法
代码:
1 #!/usr/bin/python3 2 3 class Animal(object): 4 pass 5 6 #大类 7 class Mammal(Animal): 8 pass 9 10 class Bird(Animal): 11 pass 12 13 14 class Runnable(object): 15 def run(self): 16 print('running...') 17 18 class Flyable(object): 19 def fly(self): 20 print('Flying...') 21 22 class Dog(Mammal, Runnable): 23 pass 24 25 class Bat(Mammal, Flyable): 26 pass
类的专有方法
形如 __xxx__ 变量或函数名在 python 中是有特殊用途的
除了前面的构造函数 __init__ 外还有:
__str__ 和 __repr__
类似于c++中重载<<运算符,将类对象按给定的格式转化成字符串
1 class Test(object): 2 def __init__(self, value='hello, world!'): 3 self.data = value 4 5 >>> t = Test() 6 >>> t 7 <__main__.Test at 0x7fa91c307190> 8 >>> print t 9 <__main__.Test object at 0x7fa91c307190> 10 11 # 看到了么?上面打印类对象并不是很友好,显示的是对象的内存地址 12 # 下面我们重构下该类的__repr__以及__str__,看看它们俩有啥区别 13 14 # 重构__repr__ 15 class TestRepr(Test): 16 def __repr__(self): 17 return 'TestRepr(%s)' % self.data 18 19 >>> tr = TestRepr() 20 >>> tr 21 TestRepr(hello, world!) 22 >>> print tr 23 TestRepr(hello, world!) 24 25 # 重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了 26 27 # 重构__str__ 28 calss TestStr(Test): 29 def __str__(self): 30 return '[Value: %s]' % self.data 31 32 >>> ts = TestStr() 33 >>> ts 34 <__main__.TestStr at 0x7fa91c314e50> 35 >>> print ts 36 [Value: hello, world!] 37 38 # 你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了
这部分代码摘自:https://blog.csdn.net/luckytanggu/article/details/53649156
通常__str__() 和 __repr__() 的代码是一样的,所以可以写成:
1 #!/usr/bin/python3 2 3 class Student(object): 4 def __init__(self, name): 5 self.name = name 6 7 def __str__(self): 8 return '学生名称:%s' % self.name 9 10 __repr__ = __str__ 11 12 st = Student('xiao004') 13 print(st) 14 # 输出: 15 # 学生名称:xiao004
__iter__
如果想将一个类用于 for...int 循环,类似于 list 或 tuple 一样,就必须实现一个 __iter__() 方法。该方法返回一个迭代对象,python 的 for 循环会不断调用迭代对象的 __next__() 方法,获得循环的下一个值,知道遇到 StopIteration 错误时退出循环:
1 #!/usr/bin/python3 2 3 class Fib(object): 4 def __init__(self): 5 self.a, self.b = 0, 1 6 7 def __iter__(self): 8 return self#实例本身就是迭代对象,故返回自己 9 10 def __next__(self): 11 self.a, self.b = self.b, self.a + self.b 12 if self.a > 100: 13 raise StopIteration();#引发一个异常 14 return self.a 15 16 for n in Fib(): 17 print(n) 18 # 输出: 19 # 1 20 # 1 21 # 2 22 # 3 23 # 5 24 # 8 25 # 13 26 # 21 27 # 34 28 # 55 29 # 89
__getitem__
上面定义的 Fib 虽然能作用于 for 循环,和 list 有点像,但不能和 list 一样用下标取元素。要像 list 一样按照下标取出元素,需要实现 __getitem__() 方法:
1 #!/usr/bin/python3 2 3 class Fib(object): 4 def __init__(self): 5 self.a, self.b = 0, 1 6 7 def __getitem__(self, n): 8 a, b = 1, 1 9 for x in range(n): 10 a, b = b, a + b 11 return a 12 13 def __iter__(self): 14 return self#实例本身就是迭代对象,故返回自己 15 16 def __next__(self): 17 self.a, self.b = self.b, self.a + self.b 18 if self.a > 100: 19 raise StopIteration();#引发一个异常 20 return self.a 21 22 fib = Fib() 23 print(fib[3])#3 24 # for i in fib: 25 # print(i)
__getattr__
正常情况下,调用类的方法或属性时,如果类的方法或属性不存在就会报错。当实现了 __getattr__() 方法会动态返回一个属性.:
1 class Studet(object): 2 def __init__(self): 3 self.name = 'xiao004' 4 5 def __getattr__(self, attr): 6 if attr == 'score': 7 return 95 8 9 st = Studet() 10 print(st.score)#st并没有score属性,动态返回一个属性 11 # 输出: 12 # 95
当调用不存在的属性 score 时,python 解释器会调用 __getattr__(self,‘score’) 尝试获得属性。
注意:只有在没有找到属性的情况下才调用__getattr__。此外,如果我们调用一个不存在且 __getattr__ 中也没处理的属性,仍然会报错
__call__
Python中的函数是一级对象。这意味着Python中的函数的引用可以作为输入传递到其他的函数/方法中,并在其中被执行。
而Python中类的实例(对象)可以被当做函数对待。也就是说,我们可以将它们作为输入传递到其他的函数/方法中并调用他们,正如我们调用一个正常的函数那样。而类中__call__()
函数的意义正在于此。为了将一个类实例当做函数调用,我们需要在类中实现__call__()
方法。也就是我们要在类中实现如下方法:def __call__(self, *args)
。这个方法接受一定数量的变量作为输入。
假设x是X类的一个实例。那么调用x.__call__(1,2)
等同于调用x(1,2)
。这个实例本身在这里相当于一个函数。
这段描述摘自:https://blog.csdn.net/yaokai_assultmaster/article/details/70256621
即:实现了 __call__() 函数后能将实例本身当成一个函数使用
代码:
1 class Student(object): 2 def __init__(self, name): 3 self.name = name 4 5 def __call__(self): 6 print('名称:%s' % self.name) 7 8 stu = Student('xiao004') 9 stu()#实现了__call__()函数后能将实例本身当成一个函数使用 10 # 输出: 11 # 名称:xiao004
问题
双下划线开头的实例变量一定不能从外部访问么?
答:不是。不能从类外直接访问双下划线开头的变量是因为 python 解释器对外改变了双下划线开头的变量的名称
如:
1 class Student(object): 2 def __init__(self, name, score): 3 self.__name = name 4 self.__score = score 5 6 def info(self): 7 print('学生:%s; 分是: %s' % (self.__name, self.__score)) 8 9 stu = Student('xiao004', 100) 10 # print(stu.__score)#错误,__score是私有变量 11 print(stu._Student__score)
在此列中不能直接访问 __score 是因为 python 解释器对外把 __score 变量改成了 _Student__score,所以我们能在类外用过 _Student__score 访问 __score 变量