Python方法、属性和迭代器

方法、属性和迭代器  

          在Python语言中,存在一些特殊方法,这些方法在命名上与普通方法不同。例如,一些方法会在名字前后各加两个下划线(__method__),这种拼写方式有特殊意义,所以在命名普通方法时,千万不要用这种命名方式。如果类实现了这些方法中的某一个,那么这个方法在特殊情况下会被Python调用

一、构造方法  

        是创建对象的过程中被调用的第一个方法,用于初始化对象中需要的变量。

class Person:
    def __init__(self,name="Bill"):
        self.name=name
        print("构造方法已被调用")
    def getName(self):
        return self.name
    def setName(self,name):
        self.name=name

person = Person()
print(person.getName())
person1 = Person("Mike")
print(person1.getName())
person2 = Person("John")
print(person2.getName())

1、重写普通方法和构造方法    

        当B类继承A类时,B类就会拥有A类的所有成员变量和方法。如果B类中的方法名与A类中的方法名相同,那么B类中的方法会重写A类中的同名方法。也就是说,创建B对象,实际上是调用B类中的构造方法,而不是A类中的构造方法

#由于SongBbird类重写了Bird类的构造方法,所以BIrd类中的初始化的hungry变量不再存在了,因此,调用SongBird类的eat方法会抛出异常
class Bird:
    def __init__(self):
        self.hungry=True
    def eat(self):
        if self.hungry:
            print("已经吃了虫子")
            self.hungry=False
        else:
            print("已经吃过饭了,不饿了!")
b=Bird()
b.eat()
b.eat()

class SongBird(Bird):
    def __init__(self):
        self.sound = "向天再借五百年"
    def sing(self):
        print(self.sound)
sb = SongBird()
sb.sing()
sb.eat()

2、使用super函数  

        在子类中如果重写了超类的方法,通常需要在子类方法中调用超类的同名方法。(超类:如果C是B的子类,那么B可以称为C的父类;如果B是A的子类,而C也从A继承,只是A不是C的直接父类,那么A可以称作C的超类)
    如果要在子类中访问超类中的方法,需要使用super函数。该函数返回的对象代表超类对象,所以访问super函数返回的对象中的资源都属于超类。super函数可以不带任何参数,也可以带两个参数,第一个参数表示当前类的类型,第二个参数需要传入self。

class Animal:
    def __init__(self):
        print("Animal init")

class Bird(Animal):
    def __init__(self,hungry):
        super().__init__()
        self.hungry=hungry
    def eat(self):
        if self.hungry:
            print("已经吃了虫子了!")
            self.hungry=False
        else:
            print("已经吃过饭了,不饿了!")
b=Bird(False)
b.eat()
b.eat()
class SongBird(Bird):
    def __init__(self,hungry):
        #调用Bird类的构造方法,如果为super函数指定参数,第一个参数需要是当前类的类型(SongBird)
        super(SongBird,self).__init__(hungry)
        self.sound="向天再借五百年"
    def sing(self):
        print(self.sound)

sb=SongBird(True)
sb.sing()
sb.eat()

二、特殊成员方法  

    除了构造方法,还可以使用如下4个特殊方法定义自己的序列类。
    __len__(self):返回序列中元素的个数。使用len函数获取序列对象的长度时会调用该方法。
    __getitem__(self,key):返回与所给键对应的值。__getitem__方法的第2个参数表示(key),使用sequence[key]获取值时会调用该方法。
    __setitem__(self,key,value):设置key对应的值。使用sequence[key]=value设置序列中键对应的值时调用该方法
    __delitem__(self,key):从序列中删除键为key的key-value对。当使用del关键字删除序列中键为Key的key-value对时调用该方法。

class FactorialDict:
    def __init__(self):
        self.numDict = {}
    def factorial(self,n):
        if n==0 or n==1:
            return 1
        else:
            return n*self.factorial(n-1)
    def __getitem__(self, key):
        print("__getitem__方法被调用,key={}".format(key))
        if key in self.numDict:
            return self.factorial(self.numDict[key])
        else:
            return 0
    def __setitem__(self, key, value):
        print("__setitem__方法被调用,key={},value={}".format(key,value))
        self.numDict[key]=int(value)
    def __delitem__(self, key):
        print("__delitem__方法被调用,key={}".format(key))
        del self.numDict[key]
    def __len__(self):
        print("__len__方法被调用")
        return len(self.numDict)

d=FactorialDict()
d["4!"]=4
d["7!"]=7
d["12!"]=12
print(d["4!"])
print(d["7!"])
print(d["12!"])

print("len={}".format(len(d)))
del d["7!"]
print("len={}".format(len(d)))

三、静态方法和类方法

    Python包含三种方法:实例方法、静态方法和类方法。
    实例方法:要实例化类才可以调用。
    静态方法:在调用时根本不需要类的实例(静态方法不需要self参数)
    类方法:调用和静态方法完全一样,所不同的是,类方法和实例方法的定义方式相同,都需要一个self参数,只不过self参数含义不同。对于实例方法来说,这个self参数就代表当前类的实例,可以通过self访问对象中的方法和属性。而类方法的self参数表示类的元数据,也就是类本身,不能通过self参数访问对象中的方法和属性,只能通过self参数访问类的静态方法和静态属性

class MyClass:
    #实例化方法
    def instanceMethod(self):
        pass
    #静态方法
    @staticmethod
    def staticMethod():
        pass
    #类方法
    @classmethod
    def classMethod():
        pass
class MyClass:
    name = "Bill"
    def __init__(self):
        print("MyClass的构造方法被调用")
        #定义实例变量,静态方法和类方法不能访问
        self.value=20
    #静态方法
    @staticmethod
    def run():
        #访问MyClass中的name
        print("*",MyClass.name,"*")
        print("静态方法run被调用")

    #定义一个类方法
    @classmethod
    def do(self):
        print(self)
        print("[",self.name,"]")
        print("调用静态方法run")
        self.run()
        #在类方法中不能访问实例变量,否则会抛出异常
        print("成员方法do被调用")

    #定义实例方法
    def dol(self):
        print(self.value)
        print("<",self.name,">")
        print(self)
MyClass.run()           #调用静态方法
c=MyClass()             #创建类的实例
c.do()                  #通过类的实例调用类方法
print("MyClass2.name","=",MyClass.name)         #通过类访问静态变量
MyClass.do()            #通过类调用类方法
c.dol()                 #通过类的实例访问实例方法

总结

    1.方法调用:实例化方法只能通过实例来访问,静态方法只能通过类访问,类方法既可以通过类的实例访问,也可以通过类来访问  
    2.变量访问:通过实例定义的变量只能被实例方法访问,而直接在类中定义的静态变量既可以被实例方法访问,也可以被静态方法和类方法访问。  
    3.实例方法不能被静态方法和类方法访问,但静态方法和类方法可以被实例方法访问。  

四、迭代器:__iter__

        为什么要使用迭代器而不是用列表,列表可以获取列表的长度,也可以遍历所有的元素,且容易理解。但是是要付出代价的,列表之所以可以用索引来快速定位其中的任何一个元素,是因为列表一下子将所有的数据都装载在内存中,而且是一块连续的内存空间。当数据量比较小时,实现较为容易;当数据量很大时,会非常消耗内存资源。而迭代就不同,迭代是读取多少元素,就讲多少元素装载到内存中,不读取就不装载。
         如果在一个类中定义__iter__方法,那么这个类的实例就是一个迭代器。__iter__方法需要返回一个迭代器,所以就返回对象本身即可(self)。当对象每迭代一次,就会调用迭代器中的另外一个特殊成员方法__next__。该方法需要返回当前迭代的结果。

class RightTriangel:
    def __init__(self):
        #定义一个变量n,表示当前行数
        self.n = 1
    def __next__(self):
        r = "*" * (2 * self.n - 1)          #获取每一行字符串,长度为2*n - 1
        self.n += 1
        return r
    #该方法必须返回一个迭代器
    def __iter__(self):
        return self

rt = RightTriangel()
for e in rt :
    if len(e)>20:
        break
    print(e)
#迭代器计算斐波那契数列
class Fibonacci:
    def __init__(self):
        self.a=0
        self.b=1
    def __next__(self):
        result = self.b                                 #self.b为当前要迭代的值
        self.a , self.b = self.b , self.a + self.b      #计算斐波那契数列的下一个值
        return result                                   #返回当前迭代的值
    def __iter__(self):
        return self

fibs = Fibonacci()
for fib in fibs:
    print(fib,end=" ")
    if fib>500:
        break

 将迭代器转换为列表

        尽管迭代器很好用,但是仍然不具备某些功能。例如,通过索引获取某个元素,进行分片操作。这些操作都是列表的专利,所以在很多时候,需要将迭代器转换为列表。
    但是有很多迭代器都是无限迭代的,因此,在将迭代器转换为列表的时候,需要给迭代器能够迭代的元素限定一个范围,否则内存就会溢出。要想让迭代器停止迭代,只需要抛出StopIteration异常即可。
    通过list列表可以直接将迭代器转换为列表
    __iter__():返回对象本身
    __next__():返回下一个数据,如果没有数据了,需要抛出一个StopIteration异常

class Fibonacci:
    def __init__(self):
        self.a=0
        self.b=1
    def __next__(self):
        result = self.b                                 #self.b为当前要迭代的值
        self.a , self.b = self.b , self.a + self.b      #计算斐波那契数列的下一个值
        if result > 500: raise  StopIteration
        return result                                   #返回当前迭代的值
    def __iter__(self):
        return self

fibs1 = Fibonacci()
print(list(fibs1))
fibs2 = Fibonacci()
for fib in fibs2:
    print(fib,end=" ")

五、生成器

        如果说迭代器是以类为基础的单值产生器,那么生成器(generator)就是以函数为基础的单值生成器。也就是说,迭代器和生成器都只能一个值一个值地生产。每迭代一次,只能得到一个值。
    所不同的是,迭代器需要在类中定义__iter__和__next__方法,在使用时需要创建迭代器的实例。而生成器是通过一个函数实现的,可以直接调用。所以从某种意义上讲,生成器在使用上更简洁。

1、创建生成器  

        要定义一个生成器,首先要定义一个函数,在该函数对某个集合或迭代器进行迭代,然后使用yield语句产生当前要生成的值,这时函数会被冻结,直到调用生成器的代码继续迭代下一个值,生成器才会继续被执行

def MyGenerator():
    num = [1,2,3,4,5,6,7,8]
    for n in num:
        yield n
for i in MyGenerator():
    print(i,end=" ")

2、生成器的使用

生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。    
如果将yield n 换成print(n)就非常容易理解了,对num列表进行迭代,并输出每一个元素值。
不过这里使用了yield语句来提交当前生成的值,也就是for 循环的n的值,然后MyGenerator函数会被冻结(暂时不往下执行了),直到for循环继续下一次循环,再次对MyGenerator函数进行迭代,
MyGenerator函数才会继续执行,继续使用yield语句提交下一个要生成的值,直到最后一个元素为止

#利用生成器将一个二维的列表转换为一维的列表
L=[[1,2,3],[4,3,2],[1,2,4,5,7]]
#生成器函数
def enumList(L):
    #对二维列表进行迭代
    for i in L:
        for j in i:             #每一个i是一个一维列表
            yield j


for i in enumList(L):
    print(i,end=" ")
print()

#将生成器函数转换为列表
numList = list(enumList(L))
print(numList)
#递归生成器
#在上例中对一个二维列表进行了一维处理。要想对三维、四维甚至更多维的列表进行一维化处理,可以采用递归的方式。
#首先对多维列表进行迭代,然后判断每隔列表元素是否还是列表:如果仍然是列表,继续对该列表进行迭代;如果是一个普通的值,使用yield语句返回生成的值
L=[4,[1,2,[3,5,6]],[4,3,[1,2,[4,5]],2],[1,2,4,5,7]]
#生成器函数
def enumList(L):
    try:
        #对多维列表进行迭代
        for i in L:
            #将多维列表的每一个元素传入,如果该元素是一个列表,继续迭代
            #否则会抛出一个TypeError异常,处理异常时直接使用yield语句返回这个普通的元素值
            for ele in enumList(i):
                yield ele
    except TypeError:
        yield L

for i in enumList(L):
    print(i,end=" ")
print()

  • 57
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值