0.吐槽
今天尝试出门吃了个饭,结果被活生生冻回来了。
1.类(Class)
Python是典型的面向对象编程,而类是可以看成是对象的模板,对象是类的实例化(称为实例对象)。一个类主要由两部分组成——“属性”和“方法”。属性即变量,方法即函数。
1.1定义一个类
定义一个类只需用class xxx:
。一般是不加括号的,当然如果要继承其它类的话要括号中加入基类,后面详记。另外为了和函数加以区分,通常约定类以大写字母开头,函数以小写字母开头。一个简单的例子:
class Bird:
#属性
name = '鸟儿'
color = 'white'
weight = 10
#方法
def fly(self):
print('我在飞')
这样便定义了一个类。而对象是类的实例化,创建一个类只需
>>> a = Bird()
这样a
就是一个对象。如果想要调用对象的属性或方法,只需使用点操作符.
即可。
>>> a.name
'鸟儿'
>>> a.weight
10
>>> a.fly()
我在飞
1.2 self是什么
类中的每一个函数都需要一个self
参数,作用是把由该类生成的对象作为变量传递给函数。它的作用是明显的,因为如之前所说,类好比是一个模板,由这个模板可以生成许许多多的对象,这样在调用对象的方法的时候是需要加以区分的,self便是起到了这个作用。
例如:
class Bird:
def setName(self,name):
self.name = name
def getName(self):
print('我是{}'.format(self.name))
================ RESTART: E:/Academics/python/codes/class.py ================
>>> a = Bird()
>>> a.setName('麻雀')43
>>> b = Bird()
>>> b.setName('鹦鹉')
>>> a.getName()
我是麻雀
>>> b.getName()
我是鹦鹉
试了一下把self随便换成别的东西也是可以的,所以本质上就是占坑,习惯上用self而已。
1.3 私有变量
Python允许在类中创建私有变量,只需在变量前加上双下划线。私有变量无法被外部读取。
>>> class Bird:
__name = '鸟儿'
>>> a = Bird()
>>> a.__name
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
a.__name
AttributeError: 'Bird' object has no attribute '__name'
>>> a.name
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
a.name
AttributeError: 'Bird' object has no attribute 'name'
私有变量本质上是name mangling(名字改编),即把双下划线开头的变量进行了改名,改成了_类名__变量名
。
>>> a._Bird__name
'鸟儿'
1.4 继承
继承的规则使得我们可以在一个类的基础上进行拓展。语法为class 类名(被继承的类):
。被继承的类成为基类或父类,继承的类成为子类。例如:
>>> class Fish:
name = 'fish'
def hello(self):
print('I am a ' + self.name)
>>> class Goldfish(Fish):
color = 'gold'
>>> a = Goldfish() #实例化
>>> a.name
'fish'
>>> a.color
'gold'
>>> a.hello()
I am a fish
如果子类中定义与父类相同的方法或属性,则会自动覆盖父类对应的方法或属性。
>>> class Goldfish(Fish):
name = 'goldfish'
>>> b = Goldfish()
>>> b.hello()
I am a goldfish
继承时也可以继承多个基类,但容易出现不可预测的BUG,尽量避免。
1.5 一个有趣的现象——类对象与实例对象
一个类可以生成许多对象,称为实例对象;而类本身也可以看成对象,称为类对象。大概是下图中的关系。
类对象有着属于自身的属性和方法,而实例对象继承了类对象的属性和方法。对于属性而言,两者可以用相似的方法调用,例如
>>> class C:
count = 0
def print_num(self):
print(self.count)
>>> a=C() #生成实例对象
>>> a.count
0
>>> C.count
0
而对于方法,实例对象可以将自身最为参数传给self,但类对象不可以。若使用类对象调用方法则会缺少实例化后的参数,需要手动加入一个实例化对象。继续刚才的例子
>>> a.print_num()
0
>>> C.print_num()
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
C.print_num()
TypeError: print_num() missing 1 required positional argument: 'self'
>>> C.print_num(a)
0
一个更有趣的现象是,类对象的属性与实力对象的属性关系类似于全局变量与局部变量的关系。详细点说,如果修改类对象的属性,实例对象中的属性会随之更改;而若修改实例对象的属性,这时实力对象会创建一个属于自己的同名的属性(好比同名的局部变量),这既不会影响类对象的属性值也不会影响其他实例对象的属性值。举例如下:
#创建类对象
>>> class C:
count = 0
#创建实例对象
>>> a = C()
>>> b = C()
>>> c = C()
>>> print(a.count , b.count , c.count , C.count)
0 0 0 0
#修改某个实例对象的属性
>>> a.count = 10
>>> print(a.count , b.count , c.count , C.count)
10 0 0 0
#修改类对象的属性
>>> C.count = 100
>>> print(a.count , b.count , c.count , C.count)
10 100 100 100
可见当类对象的属性被修改后,实例对象a
的属性并未发生变化。这和全局变量与局部变量十分相似,如下:
>>> def print_n(num):
n = num
print('n = '+ str(n))
>>> n = 0
>>> print_n(10)
n = 10
>>> print(n)
0
2. Some BIFs
2.1 issubclass
调用方法issubclass(class, classinfo)
,作用是判断第一个参数(class)是否为第二个参数的子类。第二个参数可以是元组,只要class是其中一个元素的子类则返回TRUE。默认一个类是自己的子类。
>>> class A:
pass
>>> class B(A):
pass
>>> issubclass(B,A)
True
>>> issubclass(B,B)
True
>>> issubclass(B,object)
True
object
是所有类的基类。
2.2 isinstance
调用方法isinstance(object, classinfo)
,作用是判断第一个参数(object)是否为第二个参数的实例对象。如果object是classinfo的子类的实例也会返回TRUE。第二个参数可以是元组,只要object是其中一个元素的实例对象则返回TRUE。第一个参数不是对象时返回FALSE。
>>> class A:
pass
>>> class B(A):
pass
>>> a = A()
>>> b = B()
>>> isinstance(b,A)
True
>>> isinstance(a,B)
False
2.3 hasattr
调用方法hasattr(object, name)
。判断对象object里是否有属性name,第二个参数须为字符串。返回True或False。
2.4 getattr
调用方法getattr(object, name[,default])
。作用与hasattr
相似,但返回对象指定的属性值,如果属性不存在,则返回default的值。若没有设置default参数,则会抛出AttributionError异常。
>>> class A():
x=1
>>> a = A()
>>> getattr(a, 'x', '属性值不存在')
1
>>> getattr(a, 'y', '属性值不存在')
'属性值不存在'
2.5 setattr
调用方法setattr(object, name, value)
。如果属性不存在,则会创建新属性并赋值。已有属性会进行修改。
>>> setattr(a, 'y', 3)
>>> getattr(a, 'y', '属性值不存在')
3
2.6 delattr
调用方法delattr(object, name)
。用于删除属性,属性不存在会报错。
3. 魔法方法
3.1 __init__()
通常把__init__()
方法称为构造方法,它的作用是只要实例化里的对象,这个方法就会在对象被创建时自动调用,一般常用来初始化参数。
>>> class Fish:
def __init__(self, name):
self.name = name
def printname(self):
print(self.name)
>>> a = Fish('Goldfish')
>>> a.printname()
Goldfish
继承时出现的一个问题
在继承时会出现一个问题,就是子类的__init__()
会覆盖基类的__init__()
,比如
>>> class Fish():
def __init__(self, x , y):
self.x = x
self.y = y
def move(self):
self.x -= 1
self.y += 2
print(self.x, self.y)
>>> class Goldfish(Fish):
def __init__(self, name):
self.name = name
>>> a = Goldfish('金鱼')
>>> a.move()
Traceback (most recent call last):
File "<pyshell#59>", line 1, in <module>
a.move()
File "<pyshell#50>", line 6, in move
self.x -= 1
AttributeError: 'Goldfish' object has no attribute 'x'
方法一:调用未绑定的父类方法
>>> class Fish():
def __init__(self, x , y):
self.x = x
self.y = y
def move(self):
self.x -= 1
self.y += 2
print(self.x, self.y)
>>> class Goldfish(Fish):
def __init__(self,x,y,name):
Fish.__init__(self,x,y)
self.name = name
>>> a = Goldfish(1,3,'金鱼')
>>> a.x
1
>>> a.y
3
>>> a.move()
0 5
方法二:super()方法
super()
可以自动寻找基类,并且自动带入self
。
>>> class Fish():
def __init__(self, x , y):
self.x = x
self.y = y
def move(self):
self.x -= 1
self.y += 2
print(self.x, self.y)
>>> class Goldfish(Fish):
def __init__(self,x,y,name):
super().__init__(x,y)
self.name = name
>>> a = Goldfish(1,3,'金鱼')
>>> a.x
1
>>> a.y
3
>>> a.move()
0 5
先写到这吧,魔方方法太多了,下次继续。
往期回顾
Python学习笔记(0)之Hello,python!
Python学习笔记(1)之列表list
Python学习笔记(2)之元组、字典&集合
Python学习笔记(3)之字符串string
Python学习笔记(4)之函数(一)
Python学习笔记(5)之函数(二)
Python学习笔记(6)之文件document