1.对象 = 属性(变量) + 方法(函数)
属性:对象的特征;方法:对象的行为
以lzm为例写成代码
class Lzm:
# Python中的类名约定以大写字母开头
# 特征的描述称为属性,在代码层面来看其实就是变量
body = 'strong'
face = 'handsome'
arms = 2
legs = 2
mouth = 'big嘴'
ismale = True
# 方法实际就是函数,通过调用这些函数来完成某些工作
def walk(self):
print('I am walking')
def run(self):
print('I am running')
def eat(self):
print("It's delicious")
def sleep(self):
print('lzm must go to sleep early')
以上代码定义了对象的特征(属性)和行为(方法),但还不是一个完整的对象,将定义的这些称为类(class)。需要使用类来创建一个真正的对象,这个对象就称为这个类的一个实例(instance),也叫实例对象(instance objects)。
创建一个对象,也叫类的实例化。
>>> # 先运行上述类的代码
>>> a = Lzm()
>>> a.face
'handsome'
>>> a.legs
2
>>> a.walk()
I am walking
>>> a.sleep()
lzm must go to sleep early
2.面向对象编程
① self是什么:由同一个类可以生成无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么Python就知道需要操作哪个对象的方法了。
class Ball:
def setName(self, name):
self.name = name
def kick(self):
print('我叫%s,谁在踢我' % self.name)
>>> a = Ball()
>>> a.setName('lzm')
>>> b = Ball()
>>> b.setName('bnu')
>>> c = Ball()
>>> c.setName('wsm')
>>> a.kick()
我叫lzm,谁在踢我
>>> b.kick()
我叫bnu,谁在踢我
>>> c.kick()
我叫wsm,谁在踢我
'''以下代码和上述代码功能完全相同'''
class Ball:
def setName(self, n):
self.name = n
def kick(self):
print('我叫%s,谁在踢我' % self.name)
②魔法方法:可以给类增加魔力的特殊方法,如果对象实现了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python调用,而这一切都是自动发生的。
'''__init()__方法,也称为构造方法'''
class A:
def __init__(self, n):
self.name = n
def kick(self):
print('我叫%s,谁在踢我' % self.name)
>>> a = A('lzm')
>>> a.kick()
我叫lzm,谁在踢我
③公有和私有:
'''默认对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问'''
>>> class Person:
name = 'lzm'
>>> p = Person()
>>> p.name
'lzm'
'''Python中定义私有变量只需要在变量名或函数名前加上'__'两个下划线'''
>>> p = Person()
>>> p.__name
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
p.__name
AttributeError: 'Person' object has no attribute '__name'
'''在外部变量名已经隐藏起来了,理论上要访问,就需要从内部进行'''
>>> class Person:
def __init__(self, n):
self.__name = n
def getname(self):
return self.__name
>>> p = Person()
Traceback (most recent call last):
File "<pyshell#21>", line 1, in <module>
p = Person()
TypeError: __init__() missing 1 required positional argument: 'n'
>>> p = Person('lzm')
>>> p.__name
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
p.__name
AttributeError: 'Person' object has no attribute '__name'
>>> p.getname()
'lzm'
'''还有更简单的方法获得私有变量'''
>>> p._Person__name
'lzm'
3.继承
被继承的类称为基类、父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法
# 类继承语法
class 类名(被继承的类):
...
>>> class Parent:
def hello(self):
print('正在调用父类的方法...')
>>> class Child(Parent):
pass
>>> p = Parent()
>>> p.hello()
正在调用父类的方法...
>>> c = Child()
>>> c.hello()
正在调用父类的方法...
'''若子类中定义与父类同名的方法或属性,则会自动覆盖父类
对应的方法或属性'''
>>> class Parent:
def hello(self):
print('正在调用父类的方法...')
>>> class Child(Parent):
def hello(self):
print('正在调用子类的方法...')
>>> c = Child()
>>> c.hello()
正在调用子类的方法...
'example'
import random as r
class Fish:
def __init__(self):
self.x = r.randint(0, 10)
self.y = r.randint(0, 10)
def move(self):
# 这里主要演示类的继承机制,就不考虑检查场景边界和移动方向的问题
# 假设所有的鱼都是一路向西游
self.x -= 1
print('我的位置是:', self.x, self.y)
class Goldfish(Fish):
pass
class Carp(Fish): # 鲤鱼
pass
class Salmon(Fish): # 三文鱼
pass
class Shark(Fish): # 鲨鱼会多一点个性
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print('I want to eat fish!')
self.hungry = False
else:
print('涨倒了')
>>> fish = Fish()
>>> fish.move()
我的位置是: 5 3
>>> goldfish = Goldfish()
>>> goldfish.move()
我的位置是: -1 2
>>> goldfish.move()
我的位置是: -2 2
>>> shark = Shark()
>>> shark.hungry
True
>>> shark.eat()
I want to eat fish!
>>> shark.eat()
涨倒了
>>> shark.move()
Traceback (most recent call last):
File "<pyshell#56>", line 1, in <module>
shark.move()
File "<pyshell#27>", line 9, in move
self.x -= 1
AttributeError: 'Shark' object has no attribute 'x'
'''报错原因:在Shark类中重写了魔法方法__init__,但新的__init__方法里边没有初始化
鲨鱼的x坐标和y坐标,因此调用move方法就会出错。
解决办法:在鲨鱼类中重写__init__方法的时候先调用父类Fish的__init__方法。'''
# 方法一:调用未绑定的父类方法
>>> class Shark(Fish):
def __init__(self):
Fish.__init__(self)
self.hungry = True
def eat(self):
if self.hungry:
print('I want to eat fish!')
self.hungry = False
else:
print('涨倒了')
>>> shark = Shark()
>>> shark.move()
我的位置是: 5 6
>>> shark.move()
我的位置是: 4 6
# 方法二:使用super函数
>>> class Shark(Fish):
def __init__(self):
super().__init__()
self.hungry = True
def eat(self):
if self.hungry:
print('I want to eat fish!')
self.hungry = False
else:
print('涨倒了')
>>> shark = Shark()
>>> shark.move()
我的位置是: -1 0
>>> shark.move()
我的位置是: -2 0
'''super函数的'超级'之处在于:不需要明确给出任何父类的名字,它会自动找出所有
父类以及对应的办法。由于不用给出基类的名字,这就意味着如果需要改变类继承关系,
只要改变class语句里的父类即可,而不必要在大量代码中去修改所有被继承的方法。'''
4.多重继承
>>> class Base1:
def foo1(self):
print('I am foo1, in Base1')
>>> class Base2:
def foo2(self):
print('I am foo2, in Base2')
>>> class C(Base1, Base2):
pass
>>> c = C()
>>> c.foo1()
I am foo1, in Base1
>>> c.foo2()
I am foo2, in Base2
'''多重继承风险大,应谨慎使用'''
5.组合
例如:有一个乌龟类,有一个鱼类,现在要定义一个水池类,用多重继承会显得很奇怪,可以把乌龟和鱼组合成一个水池类:直接把需要的类放进去实例化就可以了。
>>> class Turtle:
def __init__(self, x):
self.num = x
>>> class Fish:
def __init__(self, x):
self.num = x
>>> class Pool:
def __init__(self, x, y):
self.turtle = Turtle(x)
self.fish = Fish(y)
def print_num(self):
print('水池里总共有乌龟%d只,小鱼%d条' % (self.turtle.num, self.fish.num))
>>> pool = Pool(1, 10)
>>> pool.print_num()
水池里总共有乌龟1只,小鱼10条
6.类、类对象和实例对象
>>> class C:
count = 0
>>> a = C()
>>> b = C()
>>> c = C()
>>> print(a.count, b.count, c.count)
0 0 0
>>> c.count += 10
>>> print(a.count, b.count, c.count)
0 0 10
>>> C.count += 100 # 注意这里是对类进行改变
>>> print(a.count, b.count, c.count)
100 100 10
'''如果属性的名字与方法名相同,属性会覆盖方法'''
>>> class C:
def x(self):
print('X-man')
>>> c = C()
>>> c.x()
X-man
>>> c.x = 1
>>> c.x
1
>>> c.x()
Traceback (most recent call last):
File "<pyshell#50>", line 1, in <module>
c.x()
TypeError: 'int' object is not callable
7.到底什么是绑定
# Python严格要求方法(函数)要有实例才能被调用,实例即self(self只是惯常写法)
# 函数内部如果要出现属性名必须带前缀 self.
>>> class BB:
def printBB(): # 没有self
print('no zuo no die')
>>> BB.printBB()
no zuo no die
>>> bb = BB()
>>> bb.printBB()
Traceback (most recent call last):
File "<pyshell#57>", line 1, in <module>
bb.printBB()
TypeError: printBB() takes 0 positional arguments but 1 was given
'''根据类实例化后的对象根本无法调用里边的函数,因为由于Python的绑定机制,
这里自动把bb对象作为第一个参数传入,所以出现了TypeError。'''
>>> class CC:
def setXY(self, x, y):
self.x = x
self.y = y
def printXY(self):
print(self.x, self.y)
>>> dd = CC()
>>> # 可以使用__dict__查看对象所拥有的属性:
>>> dd.__dict__
{}
>>> CC.__dict__
mappingproxy({'__module__': '__main__', 'setXY': <function CC.setXY at 0x0000028B07CF7730>,
'printXY': <function CC.printXY at 0x0000028B07CF77B8>,
'__dict__': <attribute '__dict__' of 'CC' objects>,
'__weakref__': <attribute '__weakref__' of 'CC' objects>,
'__doc__': None})
'''__dict__属性由一个字典组成,字典中仅有实例对象的属性,不显示类属性和特殊属性,
key表示的是属性名,value表示属性相应的数据值。'''
>>> dd.setXY(4, 5)
>>> dd.__dict__
{'x': 4, 'y': 5}
'''现在实例对象dd有了两个新属性,而且这两个属性仅属于实例对象'''
# 下面的暂时不明白,无法复现书上的
>>> CC.__dict__
mappingproxy({'__module__': '__main__', 'setXY': <function CC.setXY at 0x0000028B07CF7730>, 'printXY': <function CC.printXY at 0x0000028B07CF77B8>,
'__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__':
<attribute '__weakref__' of 'CC' objects>, '__doc__': None})
>>> del CC
>>> dd.printXY()
4 5
8.一些相关的BIF
''' (1) issubclass(class, classinfo)
如果第一个参数(class)是第二个参数(classinfo)的一个子类,
则返回True,否则返回False。
①一个类被认为是其自身的子类
②classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,
则返回True
③在其他情况下,会抛出一个TypeError异常'''
>>> class A:
pass
>>> class B(A):
pass
>>> issubclass(B, A)
True
>>> issubclass(B, B)
True
>>> issubclass(B, object)
True
>>> class C:
pass
>>> issubclass(B, C)
False
'''(2) isinstance(object, classinfo)
如果第一个参数(object)是第二个参数(classinfo)的实例对象,
则返回True,否则返回False
①如果object是classinfo的子类的一个实例,也符合条件
②如果第一个参数不是对象,则永远返回False
③classinfo可以是类对象组成的元组,只要object是其中任何一个
候选类的子类,则返回True
④如果第二个参数不是类或者由类对象组成的元组,则会抛出TypeError异常'''
>>> class A:
pass
>>> class B(A):
pass
>>> class C:
pass
>>> b1 = B()
>>> isinstance(b1, B)
True
>>> isinstance(b1, C)
False
>>> isinstance(b1, A)
True
>>> isinstance(b1, (A, B, C))
True
# Python提供了以下几个BIF用于访问对象的属性,attr是attribute(属性)的缩写
'''(3) hasattr(object, name)
测试一个对象里是否有指定的属性,object是对象,name是属性名(属性的字符串名字)'''
>>> class C:
def __init__(self, x=0):
self.x = x
>>> c1 = C()
>>> hasattr(c1, 'x')
True
'''(4) getattr(object, name[,default])
返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数)
的值;若没有设置default参数,则抛出AttributeError异常'''
>>> getattr(c1, 'x')
0
>>> getattr(c1, 'y')
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
getattr(c1, 'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1, 'y', '访问的属性不存在')
'访问的属性不存在'
'''(5) setattr(object, name, value)
如果指定的属性不存在,则会新建属性并赋值'''
>>> setattr(c1, 'y', 'lzm')
>>> c1.y
'lzm'
>>> getattr(c1, 'y')
'lzm'
'''(6) delattr(object, name)
如果属性不存在,则会抛出AttributeError异常'''
>>> delattr(c1, 'y')
>>> getattr(c1, 'y', '不存在')
'不存在'
>>> delattr(c1, 'z')
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
delattr(c1, 'z')
AttributeError: z
'''(7) property(fget=None, fset=None, fdel=None, doc=None)
作用:通过属性来设置属性'''
>>> class C:
def __init__(self, size=10):
self.size = size
def getSize(self):
return self.size
def setSize(self, value):
self.size = value
def delSize(self):
del self.size
x = property(getSize, setSize, delSize)
>>> c = C()
>>> c.x
10
>>> c.x = 12
>>> c.x
12
>>> c.size
12
>>> del c.x
>>> c.size
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
c.size
AttributeError: 'C' object has no attribute 'size'
'''property()返回一个可以设置属性的属性,如何设置属性还是需要人为来写代码。
第一个参数是获得属性的方法名,第二个参数是设置属性的方法名,第三个参数是
删除属性的方法名。'''