Python公开课第四节

面对对象
类(class):用来描述相同属性和方法的对象的集合,它定义了该集合中每个对象所共有的属性和方法。对象时类的实例。
对象:通过类定义的数据结构的实例。对象包括两个数据成员(类变量和成员变量)和方法。
类变量:类变量在整个实例化的对象中是公用的。类百纳领定义在类中且在函数体外。类变量通常不作为成员变量使用。
实例变量:定义在方法中的变量,只作用于当前对象的实例
对“类”和“对象”的使用:
类就是一个模板,模板里可以包含多个函数,函数里事项一些功能。
对象是根据模板创建的实例,,通过实例对象可以执行类中的函数。

#创建类
class Foo:
    #类中的函数
    def bar(self):
        #功能描述
        pass
#---------定毕----------
#根据Foo创建对象obj
obj=Foo()
#创建对象的时候,记得在后面加括号

注意,按照Python通用规则,Class用蟒蛇是表示(HelloWorld),而其他的obj,都用‘_’隔开(this_is_object)
类中的函数第一个参数必须是self(不一定是self,必须是指代本类的一个东西),类中定义的函数叫‘方法’

class Foo:
    def bar(self):#self指代这个类本身
        #功能描述
        print('Bar')
    def hello(self,name):
        print('i am %s'%name)
#根据Foo创建对象obj
obj=Foo()
obj.bar()#执行bar()方法
obj.hello('july')#执行hello()方法
Bar
i am july

self是个什么鬼?他是为了指代它所存在的类class之中,比如我们如果有好几个不同的obj被创建成同一个类。那么有了self,我们的class Foo就能很好的知道哪个指的是自己,不会乱。

#创建类
class Foo:
    #这里我们可以创建一个类级别的变量
    #它不会随着由此类创建的变量而变化
    name='Jan'
    def bar(self):
        print('Bar')
    def hello(self,name):
        print('you are %s'%self.name)#指代类级别上的东西
        print('I am %s'%name)
        print('\n')

#根据Foo创建对象
obj1=Foo()
obj2=Foo()
obj1.hello('August')
obj2.hello('July')
you are Jan
I am August


you are Jan
I am July

所以说,这个self就是个代指,代指自己所在的class。你可以由self点进所指class本身的函数。由此可见,self本身作为一个代词,并不一定叫做self。你也可以用个其他什么来代替。只不过必须的是这个类的所有字方法中的第一个参数:

#创建类
class Foo:
    #这里我们可以创建一个类级别的变量
    #它不会随着由此类创建的变量而变化
    name='Jan'
    def bar(july):
        print('Bar')
    def hello(july,name):#我这里把self该做july,但是只要它作为第一个参数的位置没变,它依旧是Foo的自我
        print('you are %s'%july.name)#指代类级别上的东西
        print('I am %s'%name)
        print('\n')

#根据Foo创建对象
obj1=Foo()
obj2=Foo()
obj1.hello('August')
obj2.hello('July')
you are Jan
I am August


you are Jan
I am July

构造函数:构造函数是一种特殊的方法,主要用来在创建对象时初始化对象,即为对象成员赋初始值。
跟所有的OOP语言一样,Python也是有构造函数的,默认为:

#创建类
class Foo:
    def __init__(self):#这就是构造函数,它的职责是在模型创建的初期,就完成一些动作
        #简单的就是说,自定义初始化步奏
        #同样它需要self来指代本身这个class
        self.name='Jan'        
    #这里我们可以创建一个类级别的变量
    #它不会随着由此类创建的变量而变化
    def bar(july):
        print('Bar')
    def hello(july,name):#我这里把self该做july,但是只要它作为第一个参数的位置没变,它依旧是Foo的自我
        print('you are %s'%july.name)#指代类级别上的东西
        print('I am %s'%name)
        print('\n')

#当你创建一个Foo类的时候,这个方法会被跑一遍
obj1=Foo()
#在骂我们的例子中,我们默认给self自己的name变量,赋值为'Jan'
#此刻当我们调用Foo的hello方法时,hello自己的变量被赋值为'July'
obj1.hello('July')
you are Jan
I am July

init可以带更多的参数,用以初始化我们的class本身。比如说,你要初始化一个类的时候要用到一些外部参数:

#创建类
class Foo:
    def __init__(self,name2):#你可以在这里附加上一些参数,这些参数是你创建一个Foo累的必要条件
        self.name=name2        
    #这里我们可以创建一个类级别的变量
    #它不会随着由此类创建的变量而变化
    def bar(july):
        print('Bar')
    def hello(july,name):#我这里把self该做july,但是只要它作为第一个参数的位置没变,它依旧是Foo的自我
        print('you are %s'%july.name)#指代类级别上的东西
        print('I am %s'%name)
        print('\n')

#当你创建一个Foo类的时候,这个方法会被跑一遍
#此刻你不可以直接跑Foo(),你需要填入一个参数:name2
obj1=Foo('Feb')
#在骂我们的例子中,我们默认给self自己的name变量,赋值为'Jan'
#此刻当我们调用Foo的hello方法时,hello自己的变量被赋值为'July'
obj1.hello('July')
you are Feb
I am July

由楼上这些例子,我们可以大概知道整个Python的OOP概念了;Class(类)就是一个把一堆Object(对象)集合起来的地方。在这里,无论是变量换是方法,它们都享有基本层一级的概念。只不过,方法要做一点事儿,而变量就是一个值。

访问限制
我们刚刚看到,在调用obj的时候,可以直接调出name或者使用hello方法。那么我们杂么知道什么时候调用他们,什么时候不可以那?
在class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样就隐藏了内部的复杂。如果让内部属性不被外部访问,可以把属性的名称前加上两个下划线_ _,在Python中实例的变量名如果以 _ _开头,就变成一个私有变量(private),只有内部可以访问,外部不能访问
举个学生例子,我们可以用一个学生类来储存学生信息,但是我们在外部可以接触到name,那么我们其实就是可以修改name的,这是不安全的。

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def detail(self):
        print(self.name)
        print(self.age)
LiLei=Student('LiLei',12)
LiLei.age=20
LiLei.detail()
LiLei
20

为了防止这种篡改年龄的情况发生,为了维护着世界的和平,我们需要把关键的信息隐藏好:

class Student:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
    def detail(self):
        print(self.__name)
        print(self.__age)
LiLei=Student('LiLei',12)
LiLei.__age=20
LiLei.detail()
LiLei
12

看,如此一来,年龄就不会修改。那么如何保证安全,又能被外部修改?我么使用OOP家族传统理念:Getter+Setter

class Student(object):
    ...
    def get_name(self):
        return delf.name
    def get_age(self):
        return delf.age
    def set_age(self,age):
        self.age=age

至此,我们应该学会使用class定义我们自己的类了;
接下来,我们看看:
在Python中展现面对对象的三大特性:
面对对象的三大特性是指:封装,继承,多态。
封装
指的是把内容封装到某个地方,用于日后调用。他需要:

  • 把内容封装在某处
  • 从另一处调用被封装的内容
    通过对象直接调用
    我们可以在存完一个内容之后,在类以外的地方,通过这个类的对象,来直接‘点’调用
class Student:
    #假定我们初始化一个Student类的时候要做的就是,记录下每个学生的名字和年龄
    def __init__(self,name,age):
        self.name=name
        self.age=age
    #至此,我们用self指代类Student本身,并用name和age存下他们的年龄和名字
#此时我们新建一个学生
obj1=Student('July',18)
print(obj1.name)#直接调用obj1的name属性
print(obj1.age)#直接调用obj1对象的age属性
obj2=Student('Aug',28)
print(obj2.name)
print(obj2.age)
July
18
Aug
28

通过self间接调用
执行类中某一个方法时,通过self来调用类自己的变量

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def detail(self):
        print(self.name)
        print(self.age)

obj1=Student('July',28)
obj1.detail()
July
28

综上所述,对于面对对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。

继承
面对对象中的继承和现实中的继承相同,即:子可以继承父的内容(爸爸有的儿子都有)。
例如,每个学生都有名字和年龄,木有问题。我们可以把这个作为我们的父亲类。但是每个学生自己,可能有自己不同的‘方法’,比如,每个人有每个人不同的外号,不同的口号,不同的饮食习惯。

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def detail(self):
        print(self.name)
        print(self.age)

obj1=Student('July',28)
obj1.detail()
#然后我们创建一个小学生类,小学生额特点是LOL,sala无敌
class PrimaryStudent(Student):#因为是继承于学生类,所以我们写在括号内
    #这里我们可以不写构造函数,于是我们就是直接沿用Student类的构造函数
    def lol(self):#我们有一些新的独特方法,会被叠加起来
        print('不服sala')
    #接下来我们创建一个大学生类,大学生的特点是每个人都有妹子 
class CollegeStudent(Student):
    def __init__(self,name,age,gf):#这里,我们改写一下构造函数,于是爸爸的__init__方法被覆盖
        self.name=name
        self.age=age
        self.gf=gf
    def gf_detail(self):
        print(self.gf)
obj1=PrimaryStudent('小王',7)
obj1.detail()
obj1.lol()
obj2=CollegeStudent('王思聪',29,'张雨馨')
obj2.detail()
obj2.gf_detail()
July
28
小王
7
不服sala
王思聪
29
张雨馨

所以,对于前面的对象继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
这样可以极大地提高效率,减少代码的重复。
问题来了,如果我想认多个干爹那
Python和Java不同的就是,Python可以多继承,也就是可以认多个干爹。但是干爹多了就出问题了。继承的时候从谁开始?有两种方法分别是深度优先和广度优先

  • 当本身的类是经典类的时候,就按照深度优先方式查找继承的方法(即,找到一个爸爸继续找这个爸爸的爸爸,爸爸的爸爸。。。)
  • 当本身的类是新式的类时,就按照广度优先的方式查找(即找到一个爸爸,再找下一个爸爸,再找下一个爸爸,平辈之间查找)

那为什么要有经典类和新式类的区别?
这是个历史遗留问题,新类统一了类(class)和类型(type),其实也是社区推荐的写法。只不过很多程序员都很懒。。。

在2.2之前,比如2.1版本之中,类和类型是不相同的,如a是ClassA的一个实例,那么a._ class 返回‘class main.ClassA’,type(a)返回总是< type’instance’>。而引入新类之后,比如ClassB是个新类,b是ClassB的实例,b. class _和type(b)都是返回‘class main.ClassB’,这样就统一了。于是乎,在新版的Python中,这个经典类和新类的区别已经不存在了,都统一为广度优先。
我们先假设我们生活在Python2.2的时代:

#经典类的写法
class c1:
    pass
class c2(c1):
    pass
#新类的写法
class N1(object):
    pass
class N2(N1):
    pass

可见新类的标志就是大家的老祖宗继承一个系统级的类,叫做object。具体的我们来看看。

  • 经典类
class D:
    def bar(self):
        print('D.bar')
class C(D):
    def bar(self):
        print('c.bar')
class B(D):
    pass
class A(B,C):
    pass
a=A()
#执行bar方法时,首先在本类中找,如果A类没有,则在B类中继续找,如果B类中没有则在D类中找,如果D换是没找到则在C类中找如果换为找到则报错。
#所以查找顺序为A->B->D->C
#在上述查找bar的方法中,一旦找到则,则寻找过程立即中断便不会再继续找了
a.bar()
D.bar
  • 新类
class D(object):
    def bar(self):
        print('D.bar')
class C(D):
    def bar(self):
        print('c.bar')
class B(D):
    pass
class A(B,C):
    pass
a=A()
#执行bar方法时,首先在本类中找,如果A类没有,则在B类中继续找,如果B类中没有则在C类中找,如果C换是没找到则在D类中找如果换没找到则报错。
#所以查找顺序为A->B->C->D
#在上述查找bar的方法中,一旦找到则,则寻找过程立即中断便不会再继续找了
a.bar()
D.bar

当然了,现在而言都是广度优先,两种方法已经没区别了。
多态
Python中不支持多态也用不到多态,多态的概念应用在Java这一类强类型语言中,而Python尚属‘鸭子类型’(Duck typing)

class F1:
    pass
class S1(F1):
    def show(self):
        print('S1.self')
class S2:
    def show(self):
        print('S2.self')
def Func(obj):
    obj.show()
s1=S1()
Func(s1)
s2=S2()
Func(s2)
S1.self
S2.self

获取对象信息
当我们拿到一个对象的引用时,如何知道这个对象是什么类型,有哪些方法呢?
用type()

type(123)
print(type('hello'))
print(type(None))
print(type(abs))
print(type(S1))
int
<type 'str'>
<type 'NoneType'>
<type 'builtin_function_or_method'>
<type 'classobj'>

如何用语句判断是不是一种type呢?

print type(123)==type(456)
print type('hello')==type('456')
print type('hello')==type(123)
print type('hello')==str
print type([])==list
print type(())==tuple
True
True
False
True
True
True

用isinstance()
isinstance()可以告诉我们,一个对象是否是某种类型(包括继承关系)

class A:
    pass
class B(A):
    pass
class C(B):
    pass
a=A()
b=B()
c=C()
print isinstance(c,A)
#同样,isinstance也可当做type使用
print isinstance('abc',str)
True
True

使用dir
如果要获得一个对现的所有属性和方法,可以使用dir()函数它返回一个包含字符串的list,比如,获得一个对象的所有属性和方法。

dir('ABC')
['__add__',
 '__class__',
 '__contains__',
 '__delattr__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getnewargs__',
 '__getslice__',
 '__gt__',
 '__hash__',
 '__init__',
 '__le__',
 '__len__',
 '__lt__',
 '__mod__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmod__',
 '__rmul__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '_formatter_field_name_split',
 '_formatter_parser',
 'capitalize',
 'center',
 'count',
 'decode',
 'encode',
 'endswith',
 'expandtabs',
 'find',
 'format',
 'index',
 'isalnum',
 'isalpha',
 'isdigit',
 'islower',
 'isspace',
 'istitle',
 'isupper',
 'join',
 'ljust',
 'lower',
 'lstrip',
 'partition',
 'replace',
 'rfind',
 'rindex',
 'rjust',
 'rpartition',
 'rsplit',
 'rstrip',
 'split',
 'splitlines',
 'startswith',
 'strip',
 'swapcase',
 'title',
 'translate',
 'upper',
 'zfill']

类似_ XXX 的属性和方法在Python中有特殊用途,比如 len 方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动调用该对象的 len _()方法,所以下面的代码是等价的。

'ABC'.__len__()
3

我们自己写的类,也想调用len(obj)方法,就自己写一个_ len _()方法;

class MyClass:
    def __len__(self):
        return 100
obj=MyClass()
len(obj)
100

仅仅把属性和方法列出来是不够的,配合getattr(),setattr()以及hasattr(),我们可以直接操作一个对象的状态。

class MyObject:
    def __init__(self):
        self.x=9
    def power(self):
        return self.x*self.x
obj=MyObject()
print hasattr(obj,'x')#有没有属性x
print obj.x
print hasattr(obj,'y')#有没有属性y
setattr(obj,'y',19)#设置y属性
print hasattr(obj,'y')
print getattr(obj,'y')
print obj.y#获取y属性
True
9
False
True
19
19

可以传入一个default参数,如果属性不存在,就返回默认值

print getattr(obj,'z',404)
#也可获得对象的方法
print hasattr(obj,'power')
print getattr(obj,'power')
fn=getattr(obj,'power')#获取属性病赋值
print fn
print fn()#fn()和power()有相同的效果
print type(fn)
print type(fn())
404
True
<bound method MyObject.power of <__main__.MyObject instance at 0x00000000063E06C8>>
<bound method MyObject.power of <__main__.MyObject instance at 0x00000000063E06C8>>
81
<type 'instancemethod'>
<type 'int'>

实例属性和类属性
由于Python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量或者通过self变量:

class Student(object):
    def __init__(self,name):
        self.name=name
s=Student('Bob')
s.age=18

但是如果Student类本身需要绑定一个属性呢?可以直接在class中定义属性这种属性是类属性,归Student类所有.

class Student:
    name='Student'
s=Student()
print s.name#因为实例没有属性,所以会查找类的属性
print Student.name#打印类的属性
s.name='LiMing'#给实例绑定name属性
print s.name#实例属性优先级高于类属性优先级,因此他会屏蔽掉类的属性
print Student.name#但是类属性仍旧没有消失
del s.name#如果删除实例的属性
print s.name#再次调用name,因为没有找到实例属性,类的属性就显示出来了
Student
Student
LiMing
Student
Student

从上面的例子中可以看出,在编写程序的时候,千万不能把实例属性和类属性使用相同的名字,因为你的实例属性会屏蔽类属性,但是当你删除实例属性的时候,再使用相同的名字,访问的仍是类属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值