Python从入门到进阶(五)——语法基础(二)

类和对象

  • 对象作为程序的基本单位,将程序和数据封装于其中,以提高程序的重用性,灵活性和扩展性
  • 类是创建对象的模板,而对象是类的实例化,一个类可以创建多个对象
  • 类是对实体的抽象,是泛指,比如:动植物
  • 对象:是类的一个实例,是特例,比如猫狗

1.1 类的创建

Python语言中,使用class关键字来创建类,其创建方式如下:

class ClassName(bases)# class documentation string 类文档字符串,对类进行解释说明
	class_suite

class是关键字,bases是要继承的父类,默认继承object类
class documentation string是类文档字符串,一般用于类的注释说明
class_suite是类体,主要包含属性和方法
类、属性和方法的命名约定惯例如下:

类名表示实例的抽象,命名时首字母大写;
属性使用名词作为名字,比如name、age、weight等;
方法名一般指对属性的操作,其命名规则一般采用动词加属性名称形式,如updataName、updataAge、updataWeight等。 举例如下图:

class People:  # 类名
	name='张三'

	def undate_name(self,name)# 方法名
		self.name=name

在Python3.x中,如果没有显示指明要继承的父类,则默认继承object类。

class A:
	pass


class A():
	pass


class A(object):
	pass

Python3.x中,无需继承时,例子中三种类的创建效果一样,自动继承object类
新式类添加了一些内置属性和方法:

__name__:属性的名字
__doc__:属性的文档字符串
__get__(object):获取对象属性值的方法
__set__(object,value):设置对象属性值的方法
__delete__(object,value):删除对象属性的方法

1.2 对象的创建

类创建完之后,就应该创建该类的实例或对象了,该过程称之为实例化。当一个对象被创建后,就包含标识、属性和方法这三个方面的对象特性了。其中,对象标识用于区分不同的对象,属性和方法与类中的成员变量和成员函数相对应

people=People("李四",20,"50kg")  # 实例化一个对象

如例子所示:对象标识符为people,属性为括号中内容,方法为类中方法

1.3 类的属性

Python语言中,属性分为类级别和实例级别两种,实例级别的属性值默认共享类级别的属性值,除非显式进行赋值操作

class A():
	age=10


obj2=A()
Obj3=A()

如例子所示,存在三个实例,分别是类实例A和对象实例obj2,obj3
在情形1中,obj2和obj3这两个对象实例共享类实例A的属性age:

# 情形1
print(obj2.age,obj3.age,A.age)

在情形2中,显示修改了对象实例obj2.属性age:

# 情形2
obj2.age+=2
print(obj2.age,obj3.age,A.age)

在情形3中,修改了类实例A的属性age

# 情形3
A.age+=3
print(obj2.2age,obj3.age,A.age)

结果:

情景110 10 10
情景2:
12 10 10
情景3:
12 13 13

在情形2中已经修改了对象实例obj2的属性值age,其属性值和类实例A的属性值已经独立。而对象实例obj3的属性从来没有修改过,所以它还是和类实例A的属性值保持一致。
Python语言对于属性的设置采用“类.属性 = 值”或“实例.属性 = 值”的形式。如上例中obj2.age += 2等价于obj2.age = obj2.age + 2,该语句包含了属性获取及属性设置两个操作。
Python语言中的属性操作遵循三个规则:
(1)属性的获取是按照从下到上的顺序来查找属性;
(2)类和实例是两个完全独立的对象;
(3)属性设置是针对实例本身进行的。
类的定义由属性和方法组成,属性是对数据的封装,方法则是对类行为的封装.
属性按使用范围分为公有属性和私有属性,使用范围取决于属性名称
类的属性如下表所示

类的属性
属性类别介绍备注
公有属性类中和类外调用的属性若无特别声明,变量默认为公有属性
私有属性不能被类以外的函数调用的属性命名以双下划线"__"开始的成员变量就是私有属性,可以通过instance._ClassName__attribute方式访问
内置属性由系统在定义类的时候默认添加的命名一般由前后各两个下划线"__"组成,如__doc__等

内置属性如下表所示:

内置类属性
属性名作用
__name__类的名字
__doc__类的文档字符串
__bases__所有父类构成的元组
__dict__类的属性
__module__类定义所在的模块
__class__实例对应的类(仅新式类中)
内置实例属性
属性名作用
__class__实例对象所属的类名
__dict__实例对象的属性

1.4 类的方法

类方法包含公有方法,私有方法,类方法和静态方法

类的方法
方法类别介绍备注
公有方法不能被类直接调用,需要使用实例化对象调用
私有方法不能被外部的类和方法调用,其定义和私有属性的定义相同需要在方法的前面加上双下划线"__"
类方法能被类所调用,也能被对象所调用被classmethod函数调用或被@classmethod装饰器修饰的方法
静态方法相当于"全局方法",可以被类直接调用,也可以被所有实例化对象共享.该方法通过调用staticmethod方法或被@staticmethod装饰器修饰来声明,不需要”self“语句
def Play(self):  # 公有方法
	self.__Study()

def __Study(self):  # 私有方法
	print("私有方法")

def Breath(self):  # 类方法
	print("类方法1-Breath_A")
Breath_A=classmethod(Breath_A)

def Cry():  # 静态方法
	print("静态方法1-Cry_A")
Cry_A=staticmethod(Cry)

@classmethod  # 第二种方式定义类方法
def Breath_B(self):
	print("类方法2-Breath_B")

@staticmethod  # 第二种方式定义静态方法
def Cry_B():
	print("静态方法2-Cry_B")

类方法和静态方法原理上有以下区别:

  1. 静态方法不能使用self的方式调用
  2. 静态方法调用时会预先将类中用到的属性和方法进行加载,而类方法则是随调随用.因此,类方法相比静态方法具有不占资源的优势,但是速度不及静态方法
  3. 静态方法调用类中的属性时需要使用”类名.属性“的格式

1.5 内部类

内部类:类的内部定义的类,主要目的是为了更好抽象现实世界.
一般情况下不使用内部类,这样会使程序结构复杂,但是理解内部类有助于理解模块的调用
面例子中,People类中又定义了Father类和Mother类两个内部类。创建内部类的实例化对象,可以通过外部类的实例化对象调用内部类完成,如Lisi = Zhangsan.Father();也可以直接使用外部类名调用内部类,如Liming = People.Mother()。

class People():
	code=0


	class Father():
		code=1


	class Mother():
		code=2


zhangsan=People()
lisi=zhangsan.Father()  # 第一种实例化方法
print(lisi.code)  # 输出结果:1
liming=People.Mother()  # 第二种实例化方法
print(liming.code)  # 输出结果:2

从例子可以看出,内部类调用有两种方式

  1. 直接使用外部类调用内部类
  2. 先对外部类进行实例化,然后再实例化内部类

1.6 魔术方法

在Python语言中,所有以双下划线“_”包起来的方法,都统称为“魔术方法”。
这些方法在实例化时会自动调用,
比如“str
()”、“init()”、“del()”等。
使用魔术方法可以构造出非常优美的代码,比如将复杂的逻辑封装成简单的API等。
魔术方法中的“init()”方法一般叫做构造函数,用于初始化类的内部状态和参数。
如果不提供,Python语言会给出一个默认的“init()”方法

魔术方法中的“__ del __()”函数叫做析构函数,用于释放对象占用的资源。“del()”函数是可选的,
如果不提供,Python语言会在后台提供默认析构函数。

魔术方法中,有些可以实现属性访问控制的功能,如“getattr(self,name)”,
setattr(self,name,value)”方法等。
魔术方法举例:

class People():
	name="人"

	def __init__(self,n="非洲人"):
		self.name=n
		print("我是构造函数",self.name)  # 重写构造函数

	def __del__(self):
		print("我是析构函数",self.name)  # 重写析构函数


zhangsan=People()
lisi=People("欧美人")
zhangsan.__del__()  # 调用析构函数
print(zhangsan)
del zhangsan
# print(zhangsan) 出现错误,因为del后,对象就不存在了

如例子所示,对于这些魔术方法,在创建对象时可以自动执行。当对象自动执行析构函数“A.del()”后,对象仍然存在,但是在调用“del A”,后,对象就已经被回收删除,无法再次使用。

1.7 类间依赖关系

用实例方法执行某个功能时,如果需要使用另一个类的实例的方法来完成,则称这两个类之间存在关联关系.

class Person():
	def play(self,tools):
		tools.run()
		print("很开心,能玩游戏了")


class Computer():
	def run(self):
		print("电脑开机,可以运行")


class Phone():
	def run(self):
		print("手机开机,可以运行")


c=Computer()
phone=Phone()
p=Person()
p.play(phone)

结果:

手机开机,可以运行
很开心,能玩游戏了

1.8 类间关联关系

一个类的属性类型是另外一个类的类型,则称这两个类之间存在关联关系。根据属性是单值或多值,关联关系又分为一对一关联、一对多关联等。一对一关联举例如下。

class Boydef __init__(self,name,girlFriend=None):
		# 在初始化的时候可以给一个对象的属性设置成另一个类的对象
		self.girlFriend=girlFriend  # 一个男孩有一个女朋友
	def meal(self):
		if self.girlFriend:
			print(f"带着他的女朋友{self.girlFriend.name}去吃饭")
		else:
			print("我单身,我快乐")


class Girldef __init__(self,name):
		self.name=name
g=Girl("小红")
b=Boy("小明",g)
b.meal()

例子中代表的是一对一关联,Boy类中的属性girlFriend是Girl类的实例,这两个类之间存在一对一关联关系
一对多关联:

class School:
	def __init__(self,name):
		self.teach_list=[]  # 这里要装多个对象
		self.name=name
	
	def zhaopin(self,teach):
		self.teach_list.append(teach)
	
	def shangke(self):
		for t in self.teach_list:
			t.work()

class Teacherdef __init__(self,name):
		self.name=name
	def work(self):
		print(f"{self.name}在上课")

lnh=School("Old Boy")
t1=Teacher("Mr.Wu")
t2=Teacher("TaiBai")
t3=Teacher("NeZha")
lnh.zhaopin(t1)
lnh.zhaopin(t2)
lnh.zhaopin(t3)
lnh.shangke()	

1.9 继承关系

类继承是在已有类基础上构建新类的机制,该新建类也成为子类.子类可以增加新的属性或功能,也可以继承父类的功能.继承所描述的是"is-a"关系.

class People:
    name='people'

    def Study(self):
        print("Study...")

    def __init__(self):
        print("This is a People.")

class Japan(People):
    def __init__(self):
        print("Japanese.")
    
    def Study(self):
        print("Japan Study.")

class China(People):
    name="China"
    def Study(self):
        print("China Study.")

    def __init__(self):
        print("子类构造函数")
        People.__init__(self)

# 为防止出现歧义,尽量在类定义时候避免多继承
class Ren(China,Japan):
    pass

lisi=Japan()
zhangsan=China()
liming=Ren()
print(lisi.name)
print(zhangsan.name)
print(liming.name)
lisi.Study()
zhangsan.Study()
liming.Study()
print(Ren.name)

输出结果

Japanese.
子类构造函数
This is a People.
子类构造函数
This is a People.
people
China
China
Japan Study.
China Study.
China Study.
China

例子中,首先定义了一个People类,接着从该类派生出两个子类Japan和China,然后同时以这两个类为父类,派生出类Ren。一般来说,为防止出现歧义,尽量在类定义时候避免多继承。

在Python语言中使用继承机制时,有以下几点需要注意:
(1)子类拥有父类的属性和方法。
(2)子类可以创建自己属性和方法。
(3)子类可以对父类的方法进行覆盖实现。
(4)子类可重新定义父类中的属性。
(5)一个父类可由多个子类继承,一个子类也可继承多个父类。
(6)如果父类定义了__init__()方法,子类也定义了自己的__init__()方法并且还要使用父类的__init__()方法,
子类需要显式调用父类的__init__()方法。如果子类需要扩展父类的初始化行为,可以添加__init__()方法参数。
(7)当继承的多个父类中有相同的属性或方法时,会继承最前面的父类的属性或方法。

继承机制的出现,导致父类和子类相同行为出现的可能,以及在实际运行中的动态结果,此为多态。
多态(是从定义角度出发):同一类事物的多种形态,如猫的多种形态:白猫、黑猫、花猫等。
多态性(是从使用角度出发):同一种调用方式,不同的执行效果。具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
多态性依赖于继承机制,并且要定义统一的接口。

在程序语言中,多态实质就是定义了一个函数接口,在这个函数中定义了所有类中通用的功能。根据不同对象的相同调用接口,就得到不同的结果。下面例子是多态的实现。

class Animal():
    def run(self):
        raise AttributeError("子类必须实现这个方法")


class Cat(Animal):
    def run(self):
        print("猫在跑")


class Pig(Animal):
    def run(self):
        print("猪在跑")


class Dog(Animal):
    def run(self):
        print("狗在跑")
a=Cat()
b=Pig()
c=Dog()
a.run()
b.run()
c.run()

输出结果:

猫在跑
猪在跑
狗在跑

这种写法最大的问题就是接口不统一,有没有一种统一接口的写法呢?如下所示:

def func(n):
    n.run()
    
a = Cat()
b = Pig()
c = Dog()
func(a)
func(b)
func(c)

例子中演示了多态性定义和调用的两种方式。也体现了多态依赖的两个步骤。首先,多态来自同一个父类的继承;其次,为这些不同的子类定义相同的调用接口。
使用多态性有如下优点:
(1)以不变应万变,增加了程序的灵活性。不论对象千变万化,调用方式都相同。
(2)增加了程序的可扩展性。对于子类来说,使用者无需更改调用代码。

2 类的基本使用

你想我猜
class Nxwc(object):
    """description of class"""
    def __init__(self):
        self.kv=KeyValueSet()
        self.score=0
    # A.
    #def setup(self,sentences):
    #    #
    #    for sentence in sentences:
    #        self.append(sentence)
    
    #def append(self,sentence):
    #    cut_pos=sentence.find(' ')
    #    first_word,rest=sentence[0:cut_pos],sentence[cut_pos+1:].strip()
    #    self.kv.set(first_word,rest)

    #def guess(self,first_word):
    #    ret=self.kv.get(first_word)
    #    if ret is None:
    #        return 1,None
    #    else:
    #        return 0,set

    # B.
    #def setup(self,sentences):
    #    for sentence in sentences:
    #        cut_pos=sentence.find(' ')
    #        self.kv.set(sentence[0:cut_pos],sentence[cut_pos+1:].strip())

    #def guess(self,first_word):
    #    value=self.kv.get(first_word)
    #    return 0,value if value else (1,None)

    # C.
    #def setup(self,sentences):
    #    for sentence in sentences:
    #        cut_pos=sentence.find(' ')
    #        first_word,rest=sentence[0:cut_pos],sentence[cut_pos+1:].strip()
    #        self.kv.set(first_word,rest)

    #def guess(self,first_word):
    #    value=self.kv.get(first_word)
    #    err=0 if value else 1
    #    return err,value

    # D.
    def setup(self,sentences):
        for sentence in sentences:
            first_word,rest=self.parse_sentence(sentence)
            self.kv.set(first_word,rest)

    def parse_sentence(self,sentence):
        cut_pos=sentence.find(' ')
        return sentence[0:cut_pos],sentence[cut_pos+1:].strip()

    def guess(self,first_word):
        value=self.kv.get(first_word)
        return 0,value if value else (1,None)

    def run(self):
        self.score=0
        for first_word in self.kv.keys():
            ret=input("猜一猜下半句是什么? {} ->:".format(first_word))
            err, value=self.guess(first_word)
            if value==ret:
                print('你太厉害了,这都能猜到! +10分')
                self.score+=10
            else:
                self.score-=2
                print('哈哈,肯定猜不到得啦:{} -> {},扣除2分!'.format(first_word,value))
        print('结束了,你本次得分:',self.score)


class KeyValueSet:
    '''
    Set/Get/keys接口
    '''
    def __init__(self)->None:
        self.dict={}

    def set(self,key,value):
        self.dict[key]=value

    def get(self,key):
        return self.dict.get(key)

    def keys(self):
        return self.dict.keys()


if __name__=='__main__':
    sentences=[
        "hello world",
        'monkey king',
        'tomorrow is another day',
        "good bye"
        ]
    game=Nxwc()
    game.setup(sentences)
    game.run()


你想我猜扩展(一)

在上面的基础上。通过类的集成,可以扩展原有的类。例如继承KeyValueSet容器类,扩展功能,支持类似redis的 hset/hget/hkeys 扩展接口

class HashKeyValueSet(KeyValueSet):
    def __init__(self)-> None:
        super().__init__()

    def hset(self,hash_key,key,value):
        # A.
        hash_set=self.dict.get(hash_key,{key:value})
        hash_set[key]=value
        self.set(hash_key,hash_set)
        # B.
        #self.set(hash_key,{key:value})
        # C.
        #hash_set=self.get(hash_key)
        #if hash_set is None:
        #    hash_set={key:value}
        #else:
        #    hash_set[key]=value

        #self.set(hash_key,hash_set)


    def hget(self,hash_key,key):
        hash_set=self.get(hash_key)
        if hash_set is None:
            return None
        else:
            return hash_set.get(key)

    def hkeys(self,hash_key):
        hash_set=self.get(hash_key)
        if hash_set is None:
            return []
        else:
            return hash_set.keys()


if __name__=='__main__':
	hashset=HashKeyValueSet()
    hashset.hset('puzzle','hello','world!')
    hashset.hset('puzzle','monkey','king!')
    hashset.hset('puzzle','tomorrow','is another day')
    hashset.hset('puzzle','good','bye!')

    keys=hashset.hkeys('puzzle')
    for key in keys:
        ret=input('猜一下下半句是什么? {}->:'.format(key))
        value=hashset.hget('puzzle',key)
        if ret==value:
            print('你太厉害了,这都能猜得到!')
        else:
            print('哈哈,肯定猜不到得啦:{}->{}'.format(key,value))
你想我猜扩展(二)

还可以通过类的组合,使用组合而非继承实现类的扩展,支持类似 redis 的 hset/hget/hkeys 扩展接口

相比于上面,下面代码使用组合的方式复合KeyValueSet的功能,从而实现HashKeyValueSet。

class HashKeyValueSet2:
    def __init__(self,kvset)->None:
        super().__init__()
        self.kvset=kvset

    def hset(self,hash_key,key,value):
        #A.
        #self.kvset.set(hash_key,{key:value})
        #B.
        hash_set=self.kvset.get(hash_key)
        if hash_set is None:
            hash_set={key:value}
        else:
            hash_set[key]=value
        self.kvset.set(hash_key,hash_set)
        #C.
        #hash_set=self.kvset.get(hash_key)
        #if hash_set is None:
        #    hash_set={}
        #    hash_set[key]=value
        #    self.kvset.set(hash_key,hash_set)
        #else:
        #    hash_set[key]=value
        #    self.kvset.set(hash_key,hash_set)

    def hget(self,hash_key,key):
        hash_set=self.kvset.get(hash_key)
        if hash_set is None:
            return None
        else:
            return hash_set.get(key)

    def hkeys(self,hash_key):
        hash_set=self.kvset.get(hash_key)
        if hash_set is None:
            return []
        else:
            return hash_set.keys()

def test():
    hashset=HashKeyValueSet2(KeyValueSet())
    hashset.hset('puzzle','hello','world!')
    hashset.hset('puzzle','monkey','king!')
    hashset.hset('puzzle','tomorrow','is another day')
    hashset.hset('puzzle','good','bye!')

    keys=hashset.hkeys('puzzle')
    for key in keys:
        ret=input('猜一下下半句是什么? {}->:'.format(key))
        value=hashset.hget('puzzle',key)
        if ret==value:
            print('你太厉害了,这都能猜得到!')
        else:
            print('哈哈,肯定猜不到得啦:{}->{}'.format(key,value))


if __name__=='__main__':
	test()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值