0%

python学习指南

自动摘要: 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)

输出结果:

1
[1, 2]

当然,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)

输出结果:

1
3.0 -4.5

一定要用__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__)

输出结果:

1
2
3
14
12
{}

从上例中可以看到,类中并没有定义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__)

输出结果:

1
2
3
14
12
{'a': 3, 'b': 4}

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
runoob 说:我 10 岁。

将上面代码进行如下更改

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

输出结果:

1
ken 说:我 10 岁了,我在读 3 年级

多继承

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()函数是用于调用父类(超类)的一个方法

输出结果:

1
2
调用子类方法
调用父类方法

类属性与方法

私有属性:__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)

输出结果:

1
Vector (7,8)

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(C, self).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
3

继承之后调用父类参数:

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.

欢迎关注我的其它发布渠道