python基础之面向对象01
什么是面向对象?
所谓面向对象就是将那些具有共同特质的事物组成一个类(Class),在类中为这些特质定义一个个方法(函数)。
具体一点,比如说英雄联盟。在英雄联盟中我们可以把所有的英雄划分为一个英雄类,这个类包含血量/蓝量/攻击力/法术强度/移动速度……的基本属性,同时我们为这些英雄定义了买装备,前后左右移动,受到伤害掉血,释放技能……的功能或者方法,这些基本属性和功能就组成了一个较为完整的类。
实际上,类就像一个模版,当我们根据这个英雄类创建英雄盖伦时,就需要将模版中的某些数值变更一下(比如说盖伦是不需要蓝的),就初步形成了盖伦的雏形。这个'英雄盖伦'就是基于'英雄类'的一个'实例(Instance)'。通过这个实实在在的盖伦,我们就可以实现类中所定义的方法了,比如说买装备等等。
例子:
class Hero(object): hp=100 def move(self): print('move!!') def attack(self): print('attack') garen=Hero() garen.move() #结果 move!!
如何定义类?
在上面的例子就是定义类的方式。
在python中,定义类是通过class关键字
class class_name(object): pass
class后紧跟类的名称,类名通常使用大写字母开头,后面紧跟(object)。括号中的object表明了该类继承于哪个类(类事可以继承的)。一般所有的类都继承于object。就像Linux中的init进程,他是所有进程的父进程。而object是所有类的父类。
定义完了类,就可以根据类创建一个实例
instance_name=class_name()
--让类接收参数:
我们所定义的类并没有接收参数,而实际中,很多类都是可以接收参数的。
class hero(object): hp=100 #血量暂时设定为100 def __init__(self,mp): #mp即蓝量 self.mp=mp def move(self): print('%s move!!'%self) def attack(self): print('attack') def print_mp(self): print(self.mp) garen=hero(None)
上面代码中有一个__init__函数,当我们garen=hero(None)时,就是引用了这个函数。__init__函数中的参数第一个必须是self(在类中创建的函数第一个参数必须是self),这个self表示实例本身,self表示实例本身,self表示实例本身!!!
class hero(object): hp=100 def __init__(self,mp): self.mp=mp print(self) garen=hero(None) <__main__.hero object at 0x0337F610>
既然知道self表示实例本身,那么上例中的self就表示garen,self是不需要传值的,garen=hero(None)中的None传给了mp。而self.mp=mp则是将mp绑定到实例自身。
类就是许多特殊函数的集合,这个特殊指的是:
1每个函数的第一个参数为self,并且self不需要传值。
2每个函数只能被属于此类的实例调用,这也是面向对象的一个特色:数据封装。
除此之外类中的函数与正常函数没有差别。另外我们一般称类中的函数为类的方法
--下面我们来看一看类与实例的内存分配。
class hero(object): hp=100 def __init__(self,mp): self.mp=mp def move(self): print('move!!') def attack(self): print('attack') def print_mp(self): print("his mp is %s "%(self.mp)) garen=hero(None) ahri=hero(100) print(id(garen.mp)) print(id(ahri.mp)) #结果两个址不同 1466755488
class hero(object): hp=100 def __init__(self,mp): self.mp=mp def move(self): print('move!!') def attack(self): print('attack') def print_mp(self): print("his mp is %s "%(self.mp)) garen=hero(None) ahri=hero(100) print(id(garen.print_mp)) print(id(ahri.print_mp)) #结果两个地址是相同的 12027904
就如下图所示:

类的方法,变量都保存在自己这段内存中。而实例仅仅保存了自己的变量以及参数,实例并没有将方法复制到自己的内存地址中。
--访问限制
class hero(object): hp=100 def __init__(self,mp): self.mp=mp def move(self): print('move!!') def attack(self): print('attack') def print_mp(self): print("his mp is %s "%(self.mp)) def set_mp(self,new_mp): self.mp=new_mp garen=hero(None) ahri=hero(100)
在上例中,我们可以通过调用类的set_mp方法修改当前英雄的mp值。
ahri.set_mp(77) print(ahri.mp) 77
但我们也可以直接使用ahri.mp=77来修改,如何能限制这种在外部对实例进行修改的方式呢?
如果要实例内部变量不被外部访问,我们可以在变量前加上两个下划线‘__’
class hero(object): hp=100 def __init__(self,mp): self.__mp=mp def print_mp(self): print("his mp is %s "%(self.__mp)) def set_mp(self,new_mp): self.__mp=new_mp garen=hero(None) ahri=hero(100) print(ahri.__mp) #结果 AttributeError: 'hero' object has no attribute '__mp
这时,当我们想要对某个英雄的mp值进行修改时,只能调用hreo的set_mp方法进行修改。
这样做的好处:
1 确保了外部代码不能随意修改对象内部的状态。代码更健壮
2 我们可以在set_mp方法中对所输入的new_mp值的合法性进行判断,毕竟不能让mp值为负啊
---继承
当我们定义完成某个类时,可以再定义一个新类,新类可以继承第一个类。新类被称为子类,而被继承的类称为父类/基类/超类。
继承就是子类继承父类的属性和方法(注意是类属性和类方法)。
继承可以使子类使用父类中的方法,也可以在子类中新定义某个方法,或者在子类中覆盖父类的方法。
来看一个实例:
class animal(object): #定义一个动物类
def running(self):
print('running....')
class dog(animal): #定义一个狗类,且狗类继承动物类
def wangwang(self):
print('wang! wang!')
xiaohei=dog()
xiaohei.running()
xiaohei.wangwang()
#结果
running....
wang! wang!
我们可以看到,在上面的例子中,dog类继承animal类后,就不需要再重新定义running方法,可以直接调用animal中的running方法。这就是继承的优点之一。
如果我们在dog类中也定义一个running方法会怎样?
class animal(object):
def running(self):
print('running....')
class dog(animal):
def wangwang(self):
print('wang! wang!')
def running(self):
print('dog is running')
#结果
dog is running #父类的方法被子类同名的方法覆盖
wang! wang!
也就是,子类中的方法会覆盖父类中同名的方法!
子类如何执行父类的构造函数?
1 super(Son, self).__init()
2 Father.__init__(self)
class Animal(object):
name = 'ahah'
def __init__(self):
self.a = 'animal'
class Cat(Animal):
def __init__(self):
super(Cat,self).__init__()
# Animal.__init(self)
self.b = 'cat'
c1 = Cat()
print(c1.a) #animal
---多继承
所谓多继承就是指一个子类可以继承多个父类。大家可能会问:
1 怎么样才能实现多继承?
2 如果每个父类都有一个名称相同的方法,当子类调用该方法时最终调用的是哪一个?
1 首先来看第一个问题,怎么样才能实现多继承?
class animal(object):
def running(self):
print('running....')
class mammal(object):
def grow(self):
print('吃奶')
class dog(animal,mammal): #<-----------在括号中加入父类名称,并以逗号分隔即可
def wangwang(self):
print('wang! wang!')
def running(self):
print('dog is running')
xiaohei=dog()
xiaohei.running()
xiaohei.wangwang()
xiaohei.grow()
#结果
dog is running
wang! wang!
吃奶
注意:Python的类可以继承多个类,Java和C#中则只能继承一个类。
2 再来看第二个问题:如果每个父类都有一个名称相同的方法,当子类调用该方法时最终调用的是哪一个?或者说继承顺序是怎样的?
在回答这个问题之前,我们需要先了解经典类和新式类。
什么是经典类什么是新式类?
class class_name: #经典类的定义方法
pass
class class_name(object): #新式类的定义方法
pass
新式类较之经典类添加了很多功能,所以现在推荐用新式类。
在Python3中不管是新式类还是经典类继承顺序都是深度优先
class a(object):
def f(self):
print('a')
self.f1()
def f1(self):
print('a-f1')
class b(object):
def f1(self):
print('b')
class c(a):
def f1(self):
print('c')
class d(b):
def f(self):
print('d')
class e(c,d):
def f1(self):
print('e')
e1 = e()
e1.f()

但有个特殊情况:
如果A和B都all类的子类,则找完A以后不会找all,而是去找D。

我们可以总结出Python多继承时的优先顺序:
1 本身最大,先从自身查找
2 从左到右优先级依次降低,深度优先
3 若左父类与右父类的上层存在共同的父类,则将共同的父类及其上层移动到右父类的深度级别中
---方法
python中的方法包括普通方法/静态方法/类方法。但静态方法和类方法并不常用。
所有方法均属于类属性,也就是在内存中只有一份
普通方法:只能由实例调用,最少有一个self参数
静态方法:不需实例化就可以调用,没有默认参数
类方法:不需实例化就可以调用。最少有一个cls参数
class test(object):
def show1(self):
print('普通方法')
@staticmethod #静态方法
def show2():
print('静态方法')
@classmethod #类方法
def show3(cls):
print('类方法')
#不实例化
test.show2()
test.show3()
print('------------分隔-------------')
#实例化
a=test()
a.show1()
a.show2()
a.show3()
#结果
静态方法
类方法
------------分隔-------------
普通方法
静态方法
类方法
---属性
之前我们讲过,在类中的某个变量前加上两个下划线,就可以将这个变量“隐藏”起来,使其外部不可见。
class atm00(object):
def __init__(self,money):
self.money=money
def show_money(self):
print('the atm has %s¥ '%self.money)
a=atm00(100)
a.show_money()
a.money=120
a.show_money()
#结果
the atm has 100¥
the atm has 120¥
#很明显,我们可以直接在外部给a.money赋值,这样虽然操作简单,但是我们无法验证用户输入的money是否合法
这时,我们可以这样修改:
class atm01(object):
def __init__(self,money):
self.__money=money
def show_money(self):
print('the atm has %s¥ '%self.__money)
def set_money(self,money): #当修改money时,还可以检测用户输入的值是否合法
if not isinstance(money,int):
print("money's value must be interger")
else:
self.__money=money
a=atm01(100)
a.show_money()
a.set_money(120
通过修改,我们让代码更健壮更安全,但是却没有之前在外部直接赋值时的便捷了。有没有一种方法,即可以在外部直接赋值,有可以检测用户输入值的合法性呢?这里我们就要用到属性了!
class atm01(object):
def __init__(self,money_value):
self.__money=money_value
@property #第一种属性
def money(self):
return self.__money
@money.setter #第二种属性
def money(self,money_value):
if not isinstance(money_value,int):
print("money's value must be interger")
else:
self.__money=money_value
@money.deleter #第三种属性
def money(self):
print('no money')
a=atm01(100)
print(a.money)
a.money=120
print(a.money)
del a.money
#结果
100
120
no money
上面的代码可能会看不懂,没关系,上面仅仅是用来说明属性的重要性,接下来我们来正式的讲解一下属性。
如何定义属性?
属性共有三种。
首先来看第一种属性:
class test(object):
def step1(self):
pass
@property #定义时,在普通方法的基础上添加 @property 装饰器,该方法就成为属性
def step2(self):
return 'hello world'
a=test()
print(a.step2)
#结果
hello world
注意两点:
1 属性中仅能包含一个self参数
2 调用属性时,不需要加括号
第二种和第三种属性:
# ############### 定义 ###############
class Goods(object):
@property
def price(self):
print '@property'
@price.setter
def price(self, value):
print '@price.setter'
@price.deleter
def price(self):
print '@price.deleter'
# ############### 调用 ###############
obj = Goods()
obj.price # 执行@property 修饰的 price 方法,并获取方法的返回值
obj.price = 123 # 执行@price.setter 修饰的 price 方法,并将 123 赋值给方法的参数
del obj.price # 执行@price.deleter 修饰的 price 方法
总结一下,这三种属性作用分别为显示,修改,删除。

浙公网安备 33010602011771号