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())
View Code

构造函数

行为基本和 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)
View Code

注意:一个类中可以定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化。因此通常一个类中只定义一个构造函数

类的访问权限

在 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...
View Code

注意:派生类中也不能调用基类的私有方法

多态

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...
View Code

多重继承

多重继承的类定义:

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
View Code

类的专有方法

形如 __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输出的信息却改变了
View Code

这部分代码摘自: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
View Code

__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
View Code

__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)
View Code

__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
View Code

当调用不存在的属性 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
View Code

问题

双下划线开头的实例变量一定不能从外部访问么?

答:不是。不能从类外直接访问双下划线开头的变量是因为 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)
View Code

在此列中不能直接访问 __score 是因为 python 解释器对外把 __score 变量改成了 _Student__score,所以我们能在类外用过 _Student__score 访问 __score 变量

原文地址:https://www.cnblogs.com/geloutingyu/p/8833819.html