python 类,面向对象初探

概述:

面向对象   是一种编程方式(OOP Object Oriented  Programming). 

三大编程范式:

一、面向过程

二、函数式编程

三、面向对象编程

image

Why:

面向对象的程序的程序设计

     python里要不就用面向过程,要不就用面向对象,两种编程网络

面向对象的程序设计:

     可控性差,面向对象只解决了可扩展性的问题,一个软件包括了多个特性如图:

wpsF73A.tmp

可见,面向对象并不是万能的。

HowSad smile ( 如何使用面象对象编程)

第一步:面向对象的程序设计:OOD:

    ------找--------〉 对象归类(归纳对象共同的特征与技能,还有每个对象独有的特征。)

    先定义类: 实例化对象

EXAMPLE: 如下例子,

程序设计:

例如学校有:

# 定义学生类

共同的特征:

Country=’China’

共同的技能:查看成绩

独有的特征:ID、名字,性别,省

编程:

用def 做面向对象编程

编程:
def Student:
    country='China'
    def __init__(self,ID,NAME,SEX,PROVINCE):
        self.id=ID
        self.name=NAME
        self.sex=SEX
        self.province=PROVINCE
    def search_score(self):
        print('tell score')
    def study(self):
        print('study')

用class 面向对象编程:

例如,  恐怖份子这一类,基本上都人手一把AK47 ,  这是必须的,因为他们想捣乱, .  然后他们可以去破坏五角大楼,  还能引起国家战争, .

#defind class
class terrorist: #恐怖份子 这个类
    gun='ak47'  #都有AK47
    def trouble():  #能去破坏五角大楼
        print('destory Amercan the Pentagon')
    def warfare(self):  #能引起国家战争
        print('country  warfare %s'%self)

# print(terrorist.gun)
# terrorist.trouble()  # . 类. 点后面就可以调用 这个类的功能,  这个类能干什么.
# terrorist.warfare('Iraq,muslin')

 

WHAT?

类型与类:

Python3 中,将类型统称为

 

经典类与新式类:

在python3 当中类都是新式类(广度优先)。  而在python2.x当中新式类是class B(objcet) : pass

例如继承 B 的类    class C(B)

python2.x中的是经典类:经典类是深度优先

 

面向对象编程OOP:

类 是同一类事物特征的集合,对象是技能的结合体。

 

类的属性:

类的属性:数据属性与函数属性

调用方式通过 .点的方式就可引用类的属性

 

类属性的增、删、改、查

class Chinese:
    'this is chinese class'
    dang='中华人民'
    country='China'
    def __init__(self,name,age,gender):
        # print('I"m initailize ')
        self.name=name
        self.age=age
        self.gender=gender
        # self.sui_di_tu_tan='ok tu'
        # print('I"m over ')
    def sui_di_tu_tan(self):
        print('吐一口痰')
    def cha_dui(self):
        print('插到了前面')
    def eat_food(self,food):
        print('%s 正在吃 %s'%(self.name,food))
    def play_basketball(self,ball):
        print('%s 正在打 %s'%(self.name,ball))
# p1=Chinese('tony',19,'male')
# print(p1.__dict__) #查看对象的属性字典,这里对象只有数据属性,函数属性都是引用类的函数属性。 对象(实例)是没有自己的函数属性的。
# print(p1.gender)
# print(p1.name)
# print(p1.sui_di_tu_tan)
# p1.eat_food('baozi')
# p2=Chinese('wu sir ',100,'nianmen')
# p2.eat_food('jiucaixianbin')
# print(dir(p2))
# print(Chinese.country)
# Chinese.country='American'
# print(Chinese.country)
# p1=Chinese('tony','29','male')
# print(p1.country)
#
# Chinese.food='fang'
# print(Chinese.food)
# del Chinese.eat_food
# print(Chinese.food)
# def dance(self,wu):
#     print('%s 正在跳%s'%(self.name,wu))
# Chinese.dance=dance
# # print(Chinese.dan)
# p1.dance('tuoyiwu')

# print(Chinese.dang)
# # print(Chinese.sui_di_tu_tan())
# # print(dir(Chinese))
# Chinese.__dict__['sui_di_tu_tan']()
# Chinese.__dict__['cha_dui']('af')
# # print(Chinese.__doc__)
# print(Chinese.__class__)
p1=Chinese('liang',18,'man')
# print(p1.name)
# p1.play_basketball('backetball')
# print(p1.play_basketball)
p1.hobby='women'
# print(p1.hobby)
def aihao(self):
    print(‘women’)

p1.aihao=aihao
# print(p1.__dict__)
# p1.aihao('self')
#实例不能自己定义函数属性,因为这违反了class自动传self 的功能。
#
所以实例只能有数据属性,能定义函数属性是因为修改了__dict__
del p1.gender
# p1.gender='women'
# print(p1.gender)
# print(p1.__dict__)
# p1.gender='male'
# print(p1.gender)

class People:
    contry='China'
    l=[1,2,3]
    def __init__(self,name):
        self.name=name

p1=People('liang')
print(p1.l)
# p1.l=['a','b']
p1.l.append('cc')

p1.contry='japan'
print(People.l)
print(p1.__dict__)
print(p1.l)

 

继承:

继承是一种创建新的类:解决代码重用

定义一个电脑的类,电脑有很多的品牌,再多的牌子它们还是电脑

继承父类与子类的关系是  ‘是’  的关系,如:人是动物,狗也是动物,

都继承动物类。

#!-*- coding:utf-8 -*-
class Compute:
    def __init__(self,name,cpu,disk,mem):
        self.name=name
        self.cpu=cpu
        self.disk=disk
        self.mem=mem
    def read_file(self,cpu):
        print('cpu running read_file')
    def brand(self,name):
        print('brand is %s'%name)

class Thinkpad(Compute):
    def __init__(self,name,cpu,disk,mem,small_read):
        Compute.__init__(self,name,cpu,disk,mem)
        self.small_read=small_read
        print(small_read,disk)
    def price(self,price):
        print('price is %s'%price)

Thinkpad_x220=Thinkpad(Thinkpad,'thinkpad_x220','i7','200GB','yes',)
Thinkpad_x220.price(20000)
print(Thinkpad_x220)
print(Thinkpad.__bases__)

查看继承:

print('我是继承查看',Thinkpad.__bases__)

image

继承不是遗传

组合:(为了节省代码) 组合是‘有’的关系,电脑可以WIND7 MACOS ,但电脑不是 WIN7 MACOS

#使用组合  实现功能
class Thinkpad:
    def __init__(self,name,years,system):
        self.name=name
        self.years=years
        self.system=system
class lenvo:
    def __init__(self,name,years,system):
        self.name=name
        self.years=years
        self.system=system
class Mac:
    def __init__(self,name,years,system):
        self.name=name
        self.years=years
        self.system=system

class System:
    def __init__(self,name):
        self.name=name
        # print('oprate system is %s'%name)
oprate_sys=System(['windows 7 ','Mac OS'])
oprate_mac=System('Mac OS')
compute1=Thinkpad('x220','2010',oprate_sys)
compute2=lenvo('lenvo E430','2010',oprate_sys)
compute3=Mac('Mac Air','2010',oprate_sys)
print(compute1.system.name[0])
print(compute2.system.name[0])
print(compute3.system.name[1])
import abc
print(lenvo.__bases__)
print(Mac.__bases__)

 

接口归一化设计:

把应该有的功能实现一个集合体,然后子类来实现

wpsC360.tmp

抽象类: 上述的rais 抛出异常的方式不可取,

本质还是类,与类的区别在于,与变通类的额外的特点是:加了装饰器函数,子类必须实现他们

wps6C21.tmp

练习:

定义老师类,把老师的属性:薪资,隐藏起来,然后针对该属性开放访问接口
苑昊老师有多种癖好,把这种癖好隐藏起来,然后对外提供访问接口,而且以后还会苑昊老师培养很多其他的癖好,对外开放修改接口,可以新增癖好,并且需要保证新增的癖好都是字符串类型,否则无法增加成功。
class Teacher(object):
    __salary=25000
    __hobby=['somke','drink']
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def hobby_modify(self,mod_hobby):
        if type(mod_hobby) is str:
            Teacher.__hobby.append(mod_hobby)
            print('you hobby very special')
        else:
            print('Can\'t be number or sign')
    def salary(self):
        """隐藏属性 查看接口"""
        print('salary: %s'%Teacher.__salary)
    def hobby(self):
        return(Teacher.__hobby)

T1=Teacher('yh','23','male')
T4=Teacher('eg','22','male')
T2=Teacher.hobby(T1)
T2=T2[:]
T3=','.join(T2)
#teacher eg hobby
print('__________________________________________________')
def func1(n):
    n.name
func1(T4)
T4.salary()
T4.hobby_modify('running')
print(T4.name,T4.age,'%s habby %s'%(T4.name,T4.hobby()))
print('__________________________________________________')
# 访问隐藏属性
def func(n):
    n.salary()
func(T1)
print('teacher %s have %s'% (T1.name,T3))
mod_hobby=Teacher.hobby_modify(T1,'drive car')
print(Teacher.hobby(T1))
print('----------------------------')

 

 

多态与多态性:

一种事物 不同的形态,多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)

##一切皆文件:多态 与多态性练习题
import abc
class All_file(metaclass=abc.ABCMeta):
@abc.abstractmethod
def all(self):
pass

class disk(All_file):
def all(self):
print('disk is file')

class process(All_file):
def all(self):
print('process is file')

class Txt(All_file):
def all(self):
print('Txt file is file')

d1=Txt()
d2=disk()
# print(d1.all())
def func(d1):
d1.all()
func(d1)
func(d2)

多态性:

多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。

多态性分为静态多态性和动态多态性

多态性是‘一个接口(函数func),多种实现(如f.click())

派生:

继承是子类继承父类,但是子类都有自己类的特征,这个就叫做“派生”。如果子类有自已的特征且与父类的特征有重名,就找自己的特征

但是又要在父类的基础上增加自己的功能,那就是重用父类的特征加上自己的特征。

在子类中调用父类的特征。

 

封装:

封装:

第一层封装:

第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装

第二层封装:

__名字(双下划线),这种语法,只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果。

在python中用双下划线的方式实现隐藏属性(设置成私有的)

要想不让外部访问需要用到一个函数__getattr__ 详细见 进阶篇

 

特性:

property 是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

import math
class Circle:
    def __init__(self,radius):
        self.radius=radius

    @property
    def area(self):
        return math.pi * self.radius**2  #计算机面积
    @property
    def perimeter(self):
        return 2**math.pi*self.radius #计算周长
c=Circle(10)
print(c.area)  #可以像访问 “数据属性” 一样去访问area ,一访问就会触发一个函数的执行,动态计算出一个值
print(c.perimeter)  #和上面一样
----------------------------------------------------------
如果不用porperty  , 那访问就不能像访问数据属性一样去访问了

class Circle:
    def __init__(self,radius):
        self.radius=radius

    def area(self):
        return math.pi * self.radius**2
    def perimeter(self):
        return 2**math.pi*self.radius
c=Circle(10)   
print(c.area)
print(c.perimeter)

使用了property 以后不能被赋值:

image

不使用porperty 可以被赋值:

image

 

为什么要用property:

将一个类的函数定义成特性以后,对象再去使用的时候  obj.name根本无法察觉自己的name是执行了一个函数,这种特性的使用方式遵循了

 

统一访问的原则

除此之外:

面向对象的封装有三种方式:

[public]

    这种其实就是不封装,是对外公开的

[protected]

    这种封装方式对外不公开,但对朋友或者子类公开

[private]

             这种封装对谁都不公开

python 没有将以上三个内建到自己的class机制中,在python中通过porperty  实现。

class Foo:
    def __init__(self,val):
        self.__name=val  #将数据属性都隐藏起来
    @property
    def name(self): #执行查询操作此函数运行
        print('我是查看')
        return self.__name  #f.name 访问的是self.__name(这才是真实值的存放位置)
    @name.setter
    def name(self,value): #执行设置操作此函数运行(设置就是赋值)
        print('我是设置')
        if not isinstance(value,str):
            raise TypeError('must be str')
        self.__name=value
    @name.deleter #执行删除操作此函数运行
    def name(self):
        print('我是删除')
        raise TypeError('Can not delete ')

f=Foo('tony')
print(f.name)
 
从外面访问不到类里的数据属性 print(Foo.__name)

通过对象才能访问到类的数据属性 

print(f.name)

这就是封装

查询:

image

删除:

image

设置:

UCLQT4%Z{7LFBPHACDQ@Z69

练习:

 
#!/usr/bin/env python
#!-*- coding:utf-8 -*-
def write_func():
    with open('user.db','w') as file_write:
        file_write.write(str({'tony':{"password":"123",'status':False,'timeout':0},'liang':{"password":"456",'status':False,'timeout':0},}))
def read_func():
    with open('user.db','r') as file_read:
        f=file_read.read()
        data=eval(f)
        print(data['tony']['status'])
        print(data['liang']['status'])
# write_func()
# read_func()


#要求三:分析下述代码的执行流程
class User:
    db_path='user.db'
    def __init__(self,username):
        self.username=username

    @property
    def db(self):
        data=open(self.db_path,'r').read()
        return eval(data)
    # @setattr()
    # def db(self):
    #     raise TypeError:("cat't delete "
# u=User('tony')
# print(u.db['tony']['age'])
import time
class User:
    db_path='user.db'
    def __init__(self,name):
        self.name=name
    @property
    def db(self):
        with open(self.db_path,'r') as locked_check:
            info=locked_check.read()
            data=eval(info)
            return 'locked time %s '%data[self.name]['locked_time']

    # @property
    # def db(self):
    #     with open(self.db_path,'r') as read_file:
    #         info=read_file.read()
    #         return eval(info)
    @db.setter
    def db(self,value):
        with open(self.db_path,'w') as write_file:
            write_file.write(str(value))
            write_file.flush()
    def login(self):
        data=self.db
        if data[self.name]['status']:
            print('已经登录')
            return True
        if data[self.name]['timeout'] < time.time():
            count=0
            while count < 3:
                passwd=input('password>>: ')
                if not passwd:continue
                if passwd == data[self.name]['password']:
                    data[self.name]['status']=True
                    data[self.name]['timeout']=0
                    self.db=data
                    break
                count+=1
            else:
                data[self.name]['timeout']=time.time()+10
                if data[self.name]['status']:
                    return
                else:
                    data[self.name]['locked_time']=time.ctime()
                    print('locked time %s'%data[self.name]['locked_time'])
                self.db=data
        else:
            print('账号已经锁定10秒')
u1=User('tony')
# u1.login()

# u2=User('liang')
# print(u1.db)

#要求四:根据上述原理,编写退出登录方法(退出前要判断是否是登录状态),自定义property,供用户查看自己账号的锁定时间
class User:
    db_path='user.db'
    def __init__(self,name):
        self.name=name
    @property
    def db(self):
        with open(self.db_path,'r') as locked_check:
            info=locked_check.read()
            data=eval(info)
            return 'locked time %s '%data[self.name]['locked_time']

    @property
    def db(self):
        with open(self.db_path,'r') as read_file:
            info=read_file.read()
            return eval(info)
    @db.setter
    def db(self,value):
        with open(self.db_path,'w') as write_file:
            write_file.write(str(value))
            write_file.flush()
    def login(self):
        data=self.db
        if data[self.name]['status']:
            print('已经登录')
            return True
        if data[self.name]['timeout'] < time.time():
            count=0
            while count < 3:
                passwd=input('password>>: ')
                if not passwd:continue
                if passwd == data[self.name]['password']:
                    data[self.name]['status']=True
                    data[self.name]['timeout']=0
                    print('welecome')
                    self.db=data
                    break
                count+=1
            else:
                data[self.name]['timeout']=time.time()+10
                if data[self.name]['status']:
                    return
                else:
                    data[self.name]['locked_time']=time.ctime()
                    print('locked time %s'%data[self.name]['locked_time'])
                self.db=data
        else:
            print('账号已经锁定10秒')
    # @property
    def logout(self):
        with open('user.db','r') as logout_check:
            f=logout_check.read()
            data=eval(f)
            # return 'login status  "%s" '%data[self.name]['status']
            if  data[self.name]['status']:
                # return ('alread login ,logout? "y" or continue "n"')
                choice=input('alread login ,logout? "y" or continue "n": ').strip()
                if choice=='y':
                    data[self.name]['status']=False
                    self.db=data
                    print('logout')
                    # return 'logout'
                else:
                    return
# u1=User('tony')
# u1.login()
u2=User('liang')
u2.logout()
# u2.login()

 

封装与扩展性:

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码,而外部调用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供了一个良好的合作基础---或者说,只要接口这个基础约定不变,则代码改变不足虑。

class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self):  #对外提供的接口,隐藏了内部的实现细节,此时要求的是面积
        return self.__width*self.__length

r1=Room('badroom','tony',20,20,20)
print(r1.tell_area()) #使用者调用接口tell_area

再看:

class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): 
#对外提供的接口,隐藏了内部的实现细节,此时要求的是面积,,现在内部逻辑变了,而外部调用者感知不到,仍然使用该方法,但是功能已经变了
        return self.__width*self.__length*self.__high

r1=Room('badroom','tony',20,20,20)
print(r1.tell_area()) #使用者调用接口tell_area ,已经使用新功能

 

 

静态方法和类方法:

通常情况下,在类中定义的所有函数(所有 ,包括self,它只不过也是个普通的参数而已)都是对象的绑定方法,对象在调用绑定方法时会将自己做为参数传递给方法的第一个参数。除此之外还有两种常见的方法: 静态方法和类方法,二者为类量身定制,但是实例非要使用,也不会报错,后续将介绍

1、静态方法

是一个普通函数,位于类定义的 命名空间 中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法

class Foo:
    def spam(x,y,z): #类中的一个函数,self和x 没有什么区别
        print(x,y,z)
    spam=staticmethod(spam) #将spam函数做成静态方法
#简单写法
class Foo:
    @staticmethod  #等同于spam=staticmethod(spam)
    def spam(x,y,z):
        print(x,y,z)

#调用
print(type(Foo.spam))#类型本质就是函数
Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数
f1=Foo()
f1.spam(1,2,4) #实例也可以使用,但通常静态方法都是给类用的,实例在使用的时候丧失了自动传值的功能

 

#应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():#用Date.now()形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并返回
    @staticmethod
    def tommorrow(): #用Date.tommorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1929',11,23) #自定义时间
b=Date.now() #采用当前时间
c=Date.tommorrow() #采用明天时间
print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

2、类方法

类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python 为我们内置了classmethod来把类中的函数定义成类方法

class A:
    x=1
    @classmethod
    def ceshi(cls):
        print(cls,cls.x)
class B(A):
    x=2
B.ceshi()

实际应用:

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():
        t=time.localtime()
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s'%(self.year,self.month,self.day)
e=EuroDate.now()
print(e)  #这里是想触发EuroDate.__str__,但是结果是<__main__.Date object at 0x000000000113C080>
#因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod

classmethod 修改

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @classmethod
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s'%(self.year,self.month,self.day)
e=EuroDate.now()
print(e)

结果:

image

posted @ 2017-05-20 15:26  tonycloud  阅读(257)  评论(0)    收藏  举报