疯狂python讲义学习日志05——类和对象
1 引言
很久都没有更新博客了,今天特意留了时间接着之前的进度学习了类和对象在这里和大家进行分享。希望大家也能努力加油,每天都为自己的小目标添砖加瓦。
要进行面向对象编程怎么都不可能绕过类和对象了,python是完全支持面向对象的编程语言。这篇博客小编就和大家一起分享下python的类和对象的知识点。
2 类和对象
2.1 定义类
class 类名:
执行语句...
类变量...
方法...
1、python的类名必须是由一个或多个有意义的单词连缀而成,每个单词的首字母大写,其他字母小写,单词与单词之前不要使用任何连接符。
2、python可以定义空类,可以使用pass作为占位符。
class Empty:
pass
3、类中各成员之间的定义顺序没有任何影响,各成员之间可以相互调用。
4、python是一门动态编程语言:类变量可以增加|删除;实例变量也可以增加和删除。
5、在类中定义的方法默认是实例方法,实例方法的第一个参数会绑定到方法的调用者。因此,实例方法至少定义一个参数,该参数被命名为self。
6、实例方法中有一个特别的方法,init,这个方法被称为构造方法。python通过调用构造方法返回该类的对象,如果开发者没有定义任何构造函数,python会自动为该类定义一个只包含一个self参数的默认构造函数。
7、python允许为类添加说明文档,该文档同样被放在类声明之后、类体之前。
class Person:
'这是学习python定义的第一个类'
#下面定义了一个类变量
hair = 'black'
def __init__(self,name = 'Charlie',age = 8):
#下面为person对象增加两个实例变量
self.name = name
self.age=age
#下面定义一个say方法
def say(self,content):
print(content)
p = Person()
print(Person.hair)
print (p.name,p.age)
2.2 对象的动态性
1、通过为对象的新变量赋值即可添加实例的变量。
p.skills=['programming','swimming']
print(p.skills)
del p.name
print(p.name)
2、 为对象动态增加的方法,python不会自动将调用者绑定到第一个参数,程序必须手动传入参数。
def info(self):
print("--info函数--",self)
p.foo=info
p.foo(p)
3、如果动态增加的方法自动绑定第一个参数,可以借助types模块下的MethodType进行包装。
def intro_func(self,connect):
print("我是一个人,信息为:%s"% connect)
from types import MethodType
p.intro=MethodType(intro_func,p)
p.intro
4、在类体中定义的实例方法,根据第一个参数出现的位置不同,第一个参数绑定的对象不同:在构造方法中引用该构造函数正在初始化的对象;在普通实例方法中引用正在调用该方法的对象。
3 方法
3.1 类调用实例方法
1、类调用实例方法时,python不会自动为第一个参数绑定调用者,必须手动传入参数。
class User:
def walk(self):
print(self,'正慢慢地走')
User.walk()#此处报错
u=User()
User.walk(u)#正常运行
2、类调用实例方法时要求手动为第一个参数绑定参数值,并不要求绑定对应对象(称为未绑定方法)。
User.walk('hello')#正常运行
3.2 类方法与静态方法
1、类方法和静态方法的区别在于:python会自动绑定类方法的第一个参数,类方法的第一个参数(通常建议参数名为cls)会自动绑定到类本身;但对于静态方法则不会自动绑定。
2、使用@classmethod修饰的方法就是类方法;使用@staticmethod修饰的方法就是静态方法。
class Bird:
@classmethod
def fly(cls):
print('类方法fly:',cls)
@staticmethod
def info(p):
print('静态方法:',p)
#调用类方法,Bird会自动绑定到Birdl类
Bird.fly()
Bird.info('crazykit')
3.3 @函数装饰器
1、使用函数"@函数"比如函数A装饰B,实际上完成如下两部:
(1)、将被修饰的函数(函数B)作为参数传给@符号引用的函数A。
(2)、将函数B替换为第一步的返回值。
def funA(fn):
print('A')
fn()
return 'fkit'
@funA
def funB():
print('B')
print(funB)
2、(很实用)通过@符号修饰函数时python一个非常实用的功能,它既可以在被修饰函数的前面添加一些额外的逻辑处理(比如权限检查),也可以在被修饰函数的后面添加一些额外的逻辑处理(比如记录日志)。还可以在发生异常时进行一些修复工作…,完成这种改变不需要修改被修饰函数的代码,只需要添加一个修饰即可。
def auth(fn):
def auth_fn(*args):
# 用一条语句模拟权限检查
print("----------模拟执行权限检查-----------------")
# 回调被修饰的目标函数
fn(*args)
return auth_fn
@auth
def test(a,b):
print('执行test函数,参数a:%s,参数b:%s'%(a,b))
test(20,5)
4 成员变量
4.1 类变量和实例变量
1、python可以定义类变量和访问
class Address:
detail = '广州'
post_code = '510660'
def info(self):
#尝试访问类变量
#print(detail) #报错
#通过类访问类变量
print(Address.detail)
print(Address.post_code)
print(Address.detail)
addr = Address()
addr.info()
Address.detail = '福山'
Address.post_code = '24654'
addr.info()
2、python可以通过对象来访问类变量,通过对象对类变量赋值实质上是定义新的实例变量。
4.2 使用property函数定义属性
1、如果python类定义了getter、setter等访问器方法,则可使用property()函数将它们定义成属性(相当于实例变量)。
class Rectangle:
#定义构造方法
def __init__(self,width,height):
self.width = width
self.height = height
#定义setsize函数
def setsize(self,size):
self.width, self.height=size
#定义getsize函数
def getsize(self):
return self.width,self.height
#定义deletesize函数
def delsize(self):
self.width,self.height=0
#使用property定义属性
size = property(getsize,setsize,delsize,'用于描述矩形大小的属性')
print(Rectangle.size.__doc__)#访问size属性的说明文档
help(Rectangle.size)
rect = Rectangle(4,3)
print(rect.size)
rect.size=9,7
print(rect.size)
del rect.size
print(rect.size)
2、还可以利用@property装饰器修饰方法,使之成为属性。
class Cell:
@property
def state(self):
return self._state
@state.setter
def state(self,value):
if 'alive' in value.lower():
self._state = 'alive'
else:
self._state = 'dead'
#为isdead属性设置getter的方法,只有getter方法只读
@property
def is_dead(self):
return not self._state.lower() == 'alive'
c = Cell()
c.state = 'alive'
print(c._state)
print(c.is_dead)
5 隐藏和封装
1、将python类的成员命名为以双下划线开头的,python就会将它们隐藏起来。
class Uer:
def __hide(self):
print('示范隐藏的方法')
def getname(self):
return self.__name
def setname(self,name):
if len(name)<3 or len(name) >8 :
raise ValueError('用户名长度必须在3_8之间')
self.__name = name
name = property(getname,setname)
u = Uer()
u.setname('jjlll')
u.getname()
#u.name = 'fk' #不能直接访问,只能通过get,set函数访问
2、python并没有真正的隐藏机制,python会在这些方法名前添加单下划线和类名,可以针对这个规律调用隐藏的方法和属性。
class User:
def __hide(self):
print('示范隐藏的方法')
def getname(self):
return self.__name
def setname(self,name):
if len(name)<3 or len(name) >8 :
raise ValueError('用户名长度必须在3_8之间')
self.__name = name
name = property(getname,setname)
u = User()
u._User__name = 'fk' #不能直接访问,只能通过get,set函数访问
u._User__hide()
print(u.name)
6 类的继承
6.1 继承的语法
1、python子类继承父类的语法是在定义子类时,将多个父类放在子类之后的括号中。如果在定义一个python类时并未显示指定这个类的直接父类,则这个类默认继承object类。
class Fruit:
def info(self):
print('我是一个水果! 重%g克' % self.weight)
class Food:
def taste(self):
print('不同食物的口感不同')
class Apple(Fruit,Food):
pass
a = Apple()
a.weight = 5.6
a.info()
a.taste()
6.2 关于多继承
1、像c++一样,python也支持多继承,但是通常推荐:如果不是很有必要,尽量不要使用多继承,而是使用单继承。(如果存在同名方法或成员变量会遮蔽)
class Item:
def Info(self):
print("Item中方法:",'这是一个商品')
class Product:
def Info(self):
print("Product中方法:",'这是一个工业产品')
class Mouse(Item,Product):
pass
class KeyBoard(Product,Item):
pass
m = Mouse()
m.Info()
k = KeyBoard()
k.Info()
6.3 重写父类的方法
1、子类扩展了父类,可以在子类中重写父类的方法。
class Item:
def Info(self):
print("Item中方法:",'这是一个商品')
class Product:
def Info(self):
print("Product中方法:",'这是一个工业产品')
class Mouse(Item):
def Info(self):
print("Mouse方法中:",'这是一个鼠标')
m = Mouse()
m.Info()
2、可以使用未绑定方法(类调用方法)调用被重写的方法。
class Item:
def Info(self):
print("Item中方法:",'这是一个商品')
class Product:
def Info(self):
print("Product中方法:",'这是一个工业产品')
class Mouse(Item):
def Info(self):
print("Mouse方法中:",'这是一个鼠标')
Item.Info(self)
m = Mouse()
m.Info()
6.4 使用super函数调用父类的构造方法
如果子类重写了父类的构造函数,那么子类的构造函数必须要调用父类的构造函数。子类的构造函数调用父类的构造函数由两种方法:
1).使用未绑定方法
2).使用super()函数。
class Emplyee:
def __init__(self,salary):
self.salary = salary
def work(self):
print("普通员工正在写代码,工资是:",self.salary)
class Customer:
def __init__(self,favorite,address):
self.favorite = favorite
self.address = address
def info(self):
print('我是一个顾客,我的爱好是:%s,地址是%s' % (self.favorite,self.address))
class Manager(Emplyee,Customer):
def __init__(self,salary,favorite,address):
print('--Manager的构造方法---')
#使用super()调用父类的构造方法
super().__init__(salary)
#使用未绑定方法调用父类的构造方法
Customer.__init__(self,favorite,address)
m = Manager(230000,'IT产品','深圳')
m.work()
m.info()
7 python的动态性
python是动态语言,动态语言的典型特征就是:类、对象的属性、方法可以动态增加和修改。
7.1 动态属性与__slots__
1、可以为一个类的所有实例添加方法,为类动态添加方法时不需要使用MethodType进行包装,该函数第一个参数会自动绑定。
class Cat:
def __init__(self,name):
self.name = name
def walk_func(self):
print('%s 慢慢走过一片草地' % self.name)
d1 = Cat('Kitty')
d2 = Cat('MiMi')
Cat.walk = walk_func
d1.walk()
d2.walk()#自动绑定第一个参数
2、如果程序要限制为一个类动态添加属性和方法,可以通过__slots__属性来指定。__slots__属性的值是一个元组,该元组的所有元素的值列出了该类的实例允许动态添加的所有属性名和方法名,并不限制通过类来动态添加属性或方法。
class Dog:
__slots__ = ('walk','age','name')
def __init__(self,name):
self.name = name
def test():
print('预先定义的test方法')
d = Dog('snopy')
from types import MethodType
d.walk = MethodType(lambda self:print('%s 正在慢慢地走' % self.name),d)
d.age = 5
d.walk()
d.foo = 30
Dog.bar = lambda self: print('abc')
d.bar()#可以正确执行,不限制通过类添加属性和方法
7.2 使用type()函数定义类
1、当程序使用class定义类时,也可以理解为定义两个一个特殊的对象(type类的对象),因此可以使用type()函数动态创建类。使用type定义类时可指定三个参数:
参数一:创建的类名。
参数二:该类继承的父类集合(元组)。
参数三:该字典对象为该类绑定的类变量和方法。其中key就是类变量或方法名。
def fn(self):
print('fn函数')
Dog = type('Dog',(object,),dict(walk = fn,age = 6))
d = Dog()
print(type(d))
print(type(Dog))
d.walk()
print(d.age)
7.3 使用metaclass
可以使用metaclass动态修改程序中的一批类,对它们集中进行各种修改。
class ItemMetaClass(type):
#CLS代表动态修改的类
#name代表被动态修改的类名
#bases代表被动态修改的类的所有父亲
#attr代表被动态修改的所有属性、方法组成的字典
def __new__(cls,name,bases,attrs):
#为该类添加一个cal_price方法
attrs['cal_price'] = lambda self: self.price * self.discount
return type.__new__(cls,name,bases,attrs)
class Book(metaclass= ItemMetaClass):
__slots__ = ('name','price',"_discount")
def __init__(self,name,price):
self.name = name
self.price = price
@property
def discount(self):
return self._discount
@discount.setter
def discount(self,discount):
self._discount = discount
class CellPhone(metaclass= ItemMetaClass):
__slots__ = ('price',"_discount")
def __init__(self,price):
self.price = price
@property
def discount(self):
return self._discount
@discount.setter
def discount(self,discount):
self._discount = discount
b = Book('疯狂python讲义',89)
b.discount = 0.76
print(b.cal_price())
cp = CellPhone(2399)
cp.discount = 0.85
print(cp.cal_price())
8 多态
8.1 多态性
python多态实现相对简单,如下代码所示。
class Canvas:
def draw_pic(self,shape):
print('--开始画图--')
shape.draw(self)
class Rectangle:
def draw(self,canvas):
print('在%s上绘制矩形' % canvas)
class Triangle:
def draw(self,canvas):
print('在%s上绘制三角形' % canvas)
class Circle:
def draw(self,canvas):
print('在%s上绘制圆形' % canvas)
c = Canvas()
c.draw_pic(Rectangle())
c.draw_pic(Triangle())
c.draw_pic(Circle())
8.2 检查类型
1、python提供了两个函数来检查类型:
1)issubclass(cls,class_or_tuple):检查cls是否为后一个类或元组包含的多个类的任意类的子类。
2)isinstance(obj,class_or_tuple):检查cls是否为后一个类或元组包含的多个类的任意类的对象。
2、python为所有类都提供了一个__bases__属性,通过该属性可以查看该类的所有的直接父类,该属性返回所有直接父类的元组。
3、python为所有类提供了一个__subclass__()方法,该方法可以查看该类的所有直接子类,该方法返回所有子类组成的列表。
9 枚举
9.1 定义枚举
1、枚举每个成员有两个属性name和value,value代表枚举类型的序号(通常从1开始)。
import enum
Season = enum.Enum('Season',('SPRING','SUMMER','FALL','WINTER'))
2、python还为枚举提供了一个__members__属性,该属性返回一个dict字典,该字典包含了该枚举的所有枚举实例。
import enum
Season = enum.Enum('Season',('SPRING','SUMMER','FALL','WINTER'))
for name,member in Season.__members__.items():
print(name, '=>', member , ',' ,member.value)
3、还可以通过继承Enum类派生更加复杂的枚举类,可以为枚举类定义额外方法。
import enum
class Orientation(enum.Enum):
EAST = '东'
SOUTH = '南'
WEST = '西'
NORTH = '北'
def info(self):
print('这是一个代表方向 %s的枚举' % self.value)
print(Orientation.SOUTH)
print(Orientation.SOUTH.value)
print(Orientation.SOUTH.info())
9.2 枚举的构造器
可以为枚举定义构造器,定义之后再定义枚举实例时必须为构造器设置值。
import enum
class Gender(enum.Enum):
MALE = '男','阳刚之力'
FAMALE = '女','柔顺之美'
def __init__(self,cn_name,desc):
self._cn_name = cn_name
self._desc = desc
@property
def desc(self):
return self._desc
@property
def cn_name(self):
return self._cn_name
print('FEMALE的name:',Gender.FAMALE.name)
print('FEMALE的name:',Gender.FAMALE.value)
print('FEMALE的name:',Gender.FAMALE.cn_name)
print('FEMALE的name:',Gender.FAMALE.desc)
类与对象的总结至此告一段落,后面继续更新其他部分内容。