1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.html
Goto: http://www.runoob.com/python3/python3-class.htmlpython
Ref: [Advanced Python] 11 - Implement a Classc++
[面向对象]express
1、类的定义api
一个类,构造方法,self参数,“私有”属性&方法,数组
2、类的字典属性app
去查询:属性&方法 getattr()、setattr() 以及 hasattr()。函数
去模拟:字典的用法 __getattr__、__setattr__ 以及 __delattr__。post
3、类的实例this
实例的属性,类的属性
类的函数化:__call__
[类的继承]
1、单继承
调用父类的构造函数:<父类名>.__init__(self, <参数>)
2、多继承
把“父类名”改成“子类的父亲”:super(<子类名>, <类的实例>).__init__(self, *args, **kwargs)
3、类方法覆盖
多态的运用,甚至支持“鸭子类型'。
[类的”变量化“]
1、类的属性
也就是类的字典列表所得结果。
2、装饰器
@property, @xxx.setter,当作变量同样去使用。
3、重载运算符
类内部属性示范。
数据封装(Encapsulation )、继承(inheritance )和多态(polymorphism)是面向对象的三大特色。
#!/usr/bin/python3 class MyClass: """一个简单的类实例"""
i= 12345 def f(self): return 'hello world'
# 实例化类 x = MyClass() # 访问类的属性和方法 print("MyClass 类的属性 i 为:", x.i) print("MyClass 类的方法 f 输出为:", x.f())
赋值的的过程,就自动完成了类内的变量定义。
#!/usr/bin/python3 class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart
x = Complex(3.0, -4.5) print(x.r, x.i) # 输出结果:3.0 -4.5
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称。
记录了 “类的地址”,“类的属性” 等。
class Test: def prt(self): print(self) # address print(self.__class__) # name t = Test() t.prt()
执行结果:
<__main__.Test instance at 0x100771878> __main__.Test
注意:self 的名字并非规定死的,也可使用 this,可是最好仍是按照约定使用 self。
构造方法给私有属性赋值。
加上双下划线定义属性为私有属性。
#!/usr/bin/python3 #类定义 class people: #定义基本属性 name = '' age = 0
#定义私有属性,私有属性在类外部没法直接进行访问 __weight = 0
#定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name, self.age)) # 实例化类 p = people('runoob',10,30) p.speak()
加上双下划线定义属性为私有方法。
#!/usr/bin/python3 class Site: def __init__(self, name, url): self.name = name # public self.__url = url #private def who(self): print('name : ', self.name) print('url : ', self.__url) def __foo(self): # 私有方法 print('这是私有方法') def foo(self): # 公共方法 print('这是公共方法') self.__foo() x = Site('菜鸟教程', 'www.runoob.com') x.who() # 正常输出 x.foo() # 正常输出 x.__foo() # 报错
双下划线开头的实例变量是否是必定不能从外部访问呢?其实也不是。不能直接访问__name
是由于Python解释器对外把__name
变量改为了_Student__name
,因此,仍然能够经过_Student__name
来访问__name
变量:
>>> bart._Student__name 'Bart Simpson'
可是强烈建议你不要这么干,由于不一样版本的Python解释器可能会把__name
改为不一样的变量名。
总的来讲就是,Python自己没有任何机制阻止你干坏事,一切全靠自觉。
最后注意下面的这种错误写法:__name与内部解释后的_stduent__name可不是一个变量了。
>>> bart = Student('Bart Simpson', 59) >>> bart.get_name() 'Bart Simpson' >>> bart.__name = 'New Name' # 设置__name变量! >>> bart.__name 'New Name'
仅仅把属性和方法列出来是不够的,配合getattr()
、setattr()
以及hasattr()
,咱们能够直接操做一个对象的状态:
>>> hasattr(obj, 'x') # 有属性'x'吗? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有属性'y'吗? False >>> setattr(obj, 'y', 19) # 设置一个属性'y' >>> hasattr(obj, 'y') # 有属性'y'吗? True >>> getattr(obj, 'y') # 获取属性'y' 19 >>> obj.y # 获取属性'y' 19
能够传入一个default参数,若是属性不存在,就返回默认值:
>>> getattr(obj, 'z', 404) # 获取属性'z',若是不存在,返回默认值404 404
若是咱们在Class没有定义这3个方法,那么系统会用Python自带的内部函数;
若是咱们在类里面自定义了这3个函数,那么python会先调用咱们本身定义的这3个函数。
Ref: c到底干了什么?
这里要当心“无限循环”!须要经过__dict__操做。
class Cat: class_level = '贵族' def __init__(self,name,type,speed,age): self.name = name self.type = type self.speed = speed self.age = age def run(self): print('%s岁的%s%s正在以%s的速度奔跑' % (self.age, self.type, self.name, self.speed)) def __getattr__(self, item): print('你找的属性不存在') def __setattr__(self, key, value): print('你在设置属性') # self.key=value #这种方法不行,会产生无限递归了,由于他自己self.key=value也会触发__setattr__ self.__dict__[key] = value #咱们在给对象属性赋值的时候,内部原理就是操做对象的__dict__字典,因此咱们能够直接操做对象的字典实现属性赋值 #删除属性的时候会触发 def __delattr__(self, item): print('你在删除属性') # del self.item #无限递归了,和上面的__setattr__原理同样 self.__dict__.pop(item) #实例化,会到__init__函数里去给实例数据属性赋值 xiaohua = Cat('小花','波斯猫','10m/s',10)
能够把一个类的全部属性和方法调用所有动态化处理了,不须要任何特殊手段。这种彻底动态调用的特性有什么实际做用呢?
做用就是,能够针对彻底动态的状况做调用:
class 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__ # 不返回地址,返回string; __repr__能够把对象转变为字符串的形式
Output:
>>> Chain().status.user.timeline.list /status/user/timeline/list
>>> Chain().status.user /status/user
>>> Chain().status /status
动态语言的特色,二者不是一个东西。
>>> class Student(object): ... name = 'Student' ... >>> s = Student() # 建立实例s >>> print(s.name) # 打印name属性,由于实例并无name属性,因此会继续查找class的name属性 Student >>> print(Student.name) # 打印类的name属性 Student >>> s.name = 'Michael' # 给实例绑定name属性 >>> print(s.name) # 因为实例属性优先级比类属性高,所以,它会屏蔽掉类的name属性 Michael >>> print(Student.name) # 可是类属性并未消失,用Student.name仍然能够访问 Student >>> del s.name # 若是删除实例的name属性 >>> print(s.name) # 再次调用s.name,因为实例的name属性没有找到,类的name属性就显示出来了 Student
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
>>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael.
那么,怎么判断一个变量是对象仍是函数呢?
>>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False
#!/usr/bin/python3 # 父类定义 class people: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部没法直接进行访问 __weight = 0
#定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w
def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age))
#单继承示例 class student(people): grade = ''
def __init__(self, n, a, w, g): #调用父类的构函 people.__init__(self,n,a,w) # <---- 这个方法可能不是很好 self.grade = g
#覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) s = student('ken',10,60,3) s.speak()
(1) 如果父类中有相同的方法名,而在子类使用时未指定,python 从左至右 搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
(2) 子类不重写 __init__,实例化子类时,会自动调用父类定义的 __init__。
#!/usr/bin/python3 #类定义 class people: #定义基本属性 name = '' age = 0 #定义私有属性,私有属性在类外部没法直接进行访问 __weight = 0 #定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age))
#单继承示例 class student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 people.__init__(self,n,a,w) self.grade = g
#覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) #另外一个类,多重继承以前的准备 class speaker(): topic = '' name = '' def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
#多重继承 class sample(speaker, student): # <---- 多继承 a ='' def __init__(self,n,a,w,g,t): student.__init__(self, n, a, w, g) speaker.__init__(self, n, t) test = sample("Tim",25,80,4,"Python") test.speak() #方法名同,默认调用的是在括号中排前面的父类的方法
钻石继承时,c++中采用虚函数解决;python 怎么办?
参见高级部分:[Advanced Python] 11 - Implement a Class
class Father(object): def __init__(self, name, *args, **kwargs): self.name = name print("我是父类__init__")
class Son_1(Father): def __init__(self, name, age, *args, **kwargs): print("我是Son_1的__init__") super(Son_1, self).__init__(name, *args, **kwargs) self.age = age class Son_2(Father): def __init__(self, name, gender, *args, **kwargs): print("我是Son_2的__init__") self.gender = gender super(Son_2, self).__init__(name, *args, **kwargs)
class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): super(GrandSon, self).__init__(name, age, gender) def say_hello(self): print(self.name, self.age, self.gender) grand_son = GrandSon("老王", 24, "男")
经过 super(<子类名>, <子类对象>).<父类方法> 调用“被覆盖的” 父方法。
#!/usr/bin/python3 class Parent: # 定义父类 def myMethod(self): print ('调用父类方法') class Child(Parent): # 定义子类 def myMethod(self): print ('调用子类方法') c = Child() # 子类实例 c.myMethod() # 子类调用重写方法
super(Child, c).myMethod() #用子类对象调用父类已被覆盖的方法
输出:
调用子类方法
调用父类方法
类构造方法 被覆盖
这里提供了两种方式。
(1)
super(子类,self).__init__(参数1,参数2,....)
(2) 父类名称.__init__(self, 参数1,参数2,...)
animal做为父类,支持子类做为参数。
def run_twice(animal): # 参数用的是”父类“,也能够直接带入"其子类" animal.run() animal.run()
动态语言支持“鸭子类型”,具有“必要的”方法就能凑活的用了。
对于Python这样的动态语言来讲,则不必定须要传入Animal
类型。咱们只须要保证传入的对象有一个run()
方法就能够了:
class Timer(object): def run(self): print('Start...')
__init__ : 构造函数,在生成对象时调用 __del__ : 析构函数,释放对象时使用
__repr__ : 打印,转换 __setitem__ : 按照索引赋值 __getitem__ : 按照索引获取值 __len__ : 得到长度 __cmp__ : 比较运算 __call__: 函数调用
__add__ : 加运算 __sub__ : 减运算 __mul__ : 乘运算 __div__ : 除运算 __mod__ : 求余运算 __pow__ : 乘方
过去的策略
分别定义set, get函数操做。
装饰器策略
将函数当作属性来使用。
对外的api是 score 和 name。对内的变量实则 _score 和 _name。
class student(object): #新式类 def __init__(self,id): self.__id=id @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 and 100') self._score=value @property #只读 def get_id(self): return self.__id
s=student('123456') s.score=100 # 写 print(s.score) # 读 print(s.__dict__) print (s.get_id) # 只读 #s.get_id=456 #只能读,不可写: AttributeError: can't set attribute
================================================================
class A(object): # 新式类(继承自object类) def __init__(self): self.__name=None def getName(self): return self.__name def setName(self,value): self.__name=value def delName(self): del self.__name name=property(getName, setName, delName) a=A() print(a.name) # 读 a.name='python' # 写 print(a.name) # 读 del a.name # 删除 #print a.name #a.name已经被删除 AttributeError: 'A' object has no attribute '_A__name'
再一个例子:展现deleter的使用。
class test1: #经典类:没有继承object def __init__(self): self.__private='alex 1' #私有属性以2个下划线开头 #读私有属性 @property def private(self): return self.__private #尝试去写私有属性(对于经典类而言,“写”是作不到的) @private.setter def private(self,value): self.__private=value #尝试去删除私有属性(对于经典类而言,“删除”也是作不到的) @private.deleter
def private(self): del self.__private ==================================================== class test2(object):# 新式类:继承了object def __init__(self): self.__private='alex 2' #私有属性以2个下划线开头 #读私有属性 @property def private(self): return self.__private #写私有属性 @private.setter def private(self,value): self.__private=value #删除私有属性 @private.deleter def private(self): del self.__private
t1=test1() #print t1.__private # 外界不可直接访问私有属性 print (t1.private) # 读私有属性 print (t1.__dict__) t1.private='change 1' #对于经典类来讲,该语句其实是改变了实例t1的实例变量private print (t1.__dict__) print (t1.private) # 输出刚刚添加的实例变量private t1.private='change 2' print (t1.__dict__) del t1.private # 删除刚刚添加的实例变量private print (t1.__dict__) #print (t1.private) #读私有属性,由于已经删除,因此这里会报错
print ('-------------------------------------------------------') t2=test2() print (t2.__dict__) print (t2.private) # 继承了object,添加@private.setter后,才能够写 t2.private='change 2' # 修改私有属性 print (t2.__dict__) print (t2.private) del t2.private #删除私有变量 #print t2.private #私有变量已经被删除,执行“读”操做会报错:AttributeError: 'test2' object has no attribute '_test2__private' print (t2.__dict__)
关键字:__add__
#!/usr/bin/python3 class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (%d, %d)' % (self.a, self.b) def __add__(self, other): return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10) v2 = Vector(5,-2) print (v1 + v2)
执行结果:
Vector(7,8)
打印类(的字符串)
知识点:直接显示变量调用的不是__str__()
,而是__repr__()
,二者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
class people: def __init__(self,name,age): self.name=name self.age=age def __str__(self): return '这我的的名字是%s,已经有%d岁了!'%(self.name,self.age) a=people('孙悟空',999) print(a)
执行结果:
这我的的名字是孙悟空,已经有999岁了!
# 若是没有重载函数的话输出的就是一串看不懂的字符串: # <__main__.people object at 0x00000272A730D278>
以前是经过函数实现;这里则是“类的形式”,看上去比较好一些。
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例自己就是迭代对象,故返回本身 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值
# Fib()返回的是本身自己;迭代的则是__next__提供的结果。 >>> for n in Fib(): ... print(n) ... 1 1 2 3 5 ... 46368 75025
本质上仍是经过从新计算得出结果。
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3] 3 >>> f[10] 89 >>> f[100] 573147844013817084101
class Fib(object): def __getitem__(self, n):
if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a
if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L >>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
与之对应的是__setitem__()
方法,把对象视做list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素。
End.