自动摘要: python面向对象 类定义 语法格式如下: ``` classClassName: . . . <statem ……..
python面向对象
类定义
语法格式如下:
1 2 3 4 5 6
| class ClassName: <statement-1> . . . <statement-N>
|
类实例化后,可以使用其属性,实际上,创建一个类之后,可以通过类名访问其属性。
类对象和__init__
类对象支持两种操作:属性引用和示例化;属性引用使用和python中所有的属性引用一样的标准语法:obj.name;类对象创建后,类命名空间中所有的命名都是有效属性名。所以如果类定义是这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #!/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())
|
输出结果:
1 2
| MyClass类的属性 i 为: 12345 MyClass类的方法f输出为: hello world
|
以上创建一个新的类实例并将该对象赋给局部变量x,x为空的对象。
类有一个名为__init__()的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样:
1 2
| def __init__(self): self.data = [1,2]
|
类定义了__init__()方法,类的实例化操作就会自动调用__init__()。如下实例化类MyClass,对应的__init__()方法就会被调用:
1 2
| x = MyClass() print(x.data)
|
输出结果:
当然,init()方法可以有参数,参数通过__init__()传递到类的实例化操作上。例如:
1 2 3 4 5 6 7 8
| #!/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)
|
输出结果:
一定要用__init__()方法吗,不一定,例:
1 2 3 4 5 6 7 8 9 10 11 12
| class Rectangle(): def getPeri(self,a,b): return (a + b)*2
def getArea(self,a,b): return a*b
rect = Rectangle() print(rect.getPeri(3,4)) print(rect.getArea(3,4)) print(rect.__dict__)
|
输出结果:
从上例中可以看到,类中并没有定义init()方法,但是也能够得到类似的要求,结果返回了矩形实例rect的周长及面积。但是,我们通过print(rect.dict)来看这个实例的属性,竟然是空的,我定义了一个矩形,按理来说它的属性应该是它的长、宽,但是它竟然没有,这就是没有定义init()的原因了。并且,在实例化对象的时候,rect = Rectangle()参数为空,没有指定a、b的值,只有在调用函数的时候才指定了。且类中定义的每个方法的参数都有a、b,这显然浪费感情,在类中直接指定方法就可以了。因此,需要在类中定义init()方法,方便创建实例的时候,需要给实例绑定上属性,也方便类中方法(函数)的定义。上面例子用采用init()方法定义类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Rectangle(): def __init__(self,a,b): self.a = a self.b = b
def getPeri(self): return (self.a + self.b)*2
def getArea(self): return self.a * self.b
rect = Rectangle(3,4) print(rect.getPeri()) print(rect.getArea()) print(rect.__dict__)
|
输出结果:
self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别—-他们必须有一个额外的第一个参数名称,按照惯例他的名称是self。
1 2 3 4 5 6 7 8 9
| #!/usr/bin/python3
class Test: def prt(self): print(self) print(self.__class__)
t = Test() t.prt()
|
输出结果:
1 2
| <__main__.Test object at 0x0000024D70959FD0> <class '__main__.Test'>
|
从执行结果可以很明显的看出,self代表的是类的实例,代表当前对象的地址,二self.class则转向类。
self不是python关键字,我们把它换成x也是可以正常执行的:
1 2 3 4 5 6 7 8 9
| #!/usr/bin/python3
class Test: def prt(x): print(x) print(x.__class__)
t = Test() t.prt()
|
输出结果:
1 2
| <__main__.Test object at 0x000001D097EA9FD0> <class '__main__.Test'>
|
类的方法
在类的内部,使用def关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数,self代表的是类的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #!/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()
|
输出结果:
将上面代码进行如下更改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #!/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 岁,体重 %d 斤。" %(self.name,self.age,self.__weight))
#实例化类 p = people("runoob",10,60) p.speak()
|
输出结果:
1
| runoob 说:我 10 岁,体重 60 斤。
|
继承
派生类(子类)的定义如下:
1 2 3 4 5 6
| class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
|
子类(派生类DeriveaClassName)会继承父类(基类BaseClassName)的属性和方法。BaseClassName(实例中的基类名)必须与派生类定义在一个作用域内。除了类,还可以用表达式,基类定义在另一个模块中时这一点非常有用:
1
| class DerivedClassName(modname.BaseClassName):
|
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #定义类 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__(people,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()
|
输出结果:
多继承
python同样有限的支持多继承形式。多继承的类定义如下:
1 2 3 4 5 6
| class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
|
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索,即方法在子类中未找到时,从左至右查找父类中是否包含方法。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #定义类 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 岁,体重 %d 斤。" %(self.name,self.age,self.__weight))
#单继承示例 class student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 people.__init__(people,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() #方法名相同,默认调用的是在括号中参数位置排前父类的方法
|
输出结果:
1
| 我叫 Tim ,我是一个演说家,我演讲的主题是 Python
|
方法重写
如果父类方法不能满足你的需求,可以在子类重写父类的方法,实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #定义父类 class Parent: def myMedthod(self): print('调用父类方法')
#定义子类 class Child(Parent): def myMedthod(self): print('调用子类方法')
#子类实例 c = Child() #子类调用重写方法 c.myMedthod() #用子类对象调用父类已被覆盖的方法 super(Child,c).myMedthod() #super()函数是用于调用父类(超类)的一个方法
|
输出结果:
类属性与方法
私有属性:__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。类内部的方法中使用时self.__private_attrs。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class JustCounter: __secretCount = 0 #私有变量 publicCount = 0 #公开变量
def count(self): self.__secretCount += 1 self.publicCount += 1 print(self.__secretCount)
counter = JustCounter() counter.count() counter.count() print(counter.publicCount) print(counter.__secretCount) #报错,实例不能访问私有变量
|
输出结果:
1 2 3 4 5 6 7
| 1 2 2 Traceback (most recent call last): File "D:\pytorch_demo\re_demo.py", line 183, in <module> print(counter.__secretCount) #报错,实例不能访问私有变量 AttributeError: 'JustCounter' object has no attribute '__secretCount'
|
类的方法:在类的内部,使用def关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数,self代表的是类的实例。self的名字并不是规定死的,也可以用this,但是最好还是按照约定使用self.
类的私有方法:__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用,不能在类的外部调用。self.__private_method。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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() #报错,外部不能调用私有方法
|
输出结果:
1 2 3 4 5 6 7 8
| name: 菜鸟教程 url: www.runoob.com 这是公有方法 这是私有方法 Traceback (most recent call last): File "D:\pytorch_demo\re_demo.py", line 207, in <module> x.__foo() #报错,外部不能调用私有方法 AttributeError: 'Site' object has no attribute '__foo'
|
类的专有方法
- init : 构造函数,在生成对象时调用
- del : 析构函数,释放对象时使用
- repr : 打印,转换
- setitem : 按照索引赋值
- getitem: 按照索引获取值
- len: 获得长度
- cmp: 比较运算
- call: 函数调用
- add: 加运算
- sub: 减运算
- mul: 乘运算
- truediv: 除运算
- mod: 求余运算
- pow: 乘方
运算符重载
python同样支持运算符重载,可以对类的专有方法进行重载,实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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)
|
输出结果:
super
官网:python 继承 class super(type, object_or_type=None)
类型的父类或兄弟类委托方法调用的代理对象。这对于访问已在类中重写的继承方法很有用。
Object _ or _ type 确定要搜索的方法解析顺序。
例如,如果 object _ or _ type 的 _ _ mro _ _ 为 D-> B-> C-> A-> object,并且 type 的值为 B,那么 super ()搜索 C-> A-> object。
Object _ or _ type 的 _ _ mro _ _ 属性列出了 getattr ()和 super ()使用的方法解析搜索顺序。该属性是动态的,可以在更新继承层次结构时更改。
如果省略第二个参数,则返回的超级对象是未绑定的。如果第二个参数是一个对象,isinstance (obj,type)必须为 true。如果第二个参数是一个类型,issubclass (type2,type)必须为 true (这对 classmethod 很有用)。
有两个典型的 super 用例。在具有单一继承的类层次结构中,super 可用于引用父类而不显式命名它们,从而使代码更易于维护。这种使用与其他编程语言中的 super 使用非常类似。
第二个用例是支持动态执行环境中的协作多重继承。这个用例对于 Python 来说是独一无二的,在静态编译的语言或者只支持单一继承的语言中是找不到的。这使得实现多个基类实现相同方法的“菱形图”成为可能。好的设计要求这样的实现在每种情况下都具有相同的调用签名(因为调用的顺序是在运行时确定的,该顺序适应类层次结构中的变化,并且该顺序可以包括在运行时之前未知的兄弟类)。
对于这两种用例,典型的超类调用如下所示:
1 2 3
| class C(B): def method(self, arg): super().method(arg)
|
除了方法查找之外,super()还可以用于属性查找。其中一个可能的用例是调用父类或兄弟类中的描述符。
请注意,super()是作为诸如 super()之类的显式虚线属性查找的绑定过程的一部分实现的。_ _ getitem _ _ (名称)。它通过实现自己的 _ _ gettribute _ _ ()方法,以可预测的顺序搜索类,从而支持协作多重继承。因此,super()对于使用 super()[name]等语句或运算符的隐式查找是未定义的。
还要注意,除了零参数形式之外,super()并不限于使用内部方法。两个参数形式精确地指定了参数并进行了适当的引用。零参数形式仅在类定义内部工作,因为编译器填写必要的详细信息以正确检索正在定义的类,并访问普通方法的当前实例。
有关如何使用 super()设计协作类的实用建议,请参阅使用super()的指南。
基本用例:
1
| class LoggingDict(dict):
|
继承之后调用父类方法:
1 2 3 4 5 6 7 8 9
| class A: def add(self, x): y = x+1 print(y) class B(A): def add(self, x): super().add(x) b = B() b.add(2)
|
输出结果:
继承之后调用父类参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class FooParent(object): def __init__(self): self.parent = 'I\'m the parent.' print('Parent')
def bar(self, message): print(f'{message} from Parant')
class FooChild(FooParent): def __init__(self): super(FooChild, self).__init__() print('Child')
def bar(self, message): super(FooChild, self).bar(message) print('Child bar function') print(self.parent)
if __name__ == '__main__': fooChild = FooChild() fooChild.bar('hello world')
|
输出结果:
1 2 3 4 5
| Parent Child hello world from Parant Child bar function I'm the parent.
|