乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      [精]兩句話掌握 Python 最難知識(shí)點(diǎn):元類

       Levy_X 2017-11-14
      千萬(wàn)不要被所謂“元類是99%的python程序員不會(huì)用到的特性”這類的說(shuō)辭嚇住。因?yàn)槊總€(gè)中國(guó)人,都是天生的元類使用者
        學(xué)懂元類,你只需要知道兩句話:
        道生一,一生二,二生三,三生萬(wàn)物
        我是誰(shuí)?我從哪來(lái)里?我要到哪里去?
        在python世界,擁有一個(gè)永恒的道,那就是“type”,請(qǐng)記在腦海中,type就是道。如此廣袤無(wú)垠的python生態(tài)圈,都是由type產(chǎn)生出來(lái)的。
        道生一,一生二,二生三,三生萬(wàn)物。
        道 即是 type
        一 即是 metaclass(元類,或者叫類生成器)
        二 即是 class(類,或者叫實(shí)例生成器)
        三 即是 instance(實(shí)例)
        萬(wàn)物 即是 實(shí)例的各種屬性與方法,我們平常使用python時(shí),調(diào)用的就是它們。
        道和一,是我們今天討論的命題,而二、三、和萬(wàn)物,則是我們常常使用的類、實(shí)例、屬性和方法,用hello world來(lái)舉例:
        # 創(chuàng)建一個(gè)Hello類,擁有屬性say_hello ----二的起源
        class Hello():
        def say_hello(self, name='world'):
        print('Hello, %s.' % name)
        # 從Hello類創(chuàng)建一個(gè)實(shí)例hello ----二生三
        hello = Hello()
        # 使用hello調(diào)用方法say_hello ----三生萬(wàn)物
        hello.say_hello()
        輸出效果:
        Hello, world.
        這就是一個(gè)標(biāo)準(zhǔn)的“二生三,三生萬(wàn)物”過(guò)程。 從類到我們可以調(diào)用的方法,用了這兩步。
        那我們不由自主要問(wèn),類從何而來(lái)呢?回到代碼的第一行。
        class Hello其實(shí)是一個(gè)函數(shù)的“語(yǔ)義化簡(jiǎn)稱”,只為了讓代碼更淺顯易懂,它的另一個(gè)寫法是:
        def fn(self, name='world'): # 假如我們有一個(gè)函數(shù)叫fn
        print('Hello, %s.' % name)
        Hello = type('Hello', (object,), dict(say_hello=fn)) # 通過(guò)type創(chuàng)建Hello class ---- 神秘的“道”,可以點(diǎn)化一切,這次我們直接從“道”生出了“二”
        這樣的寫法,就和之前的Class Hello寫法作用完全相同,你可以試試創(chuàng)建實(shí)例并調(diào)用
        # 從Hello類創(chuàng)建一個(gè)實(shí)例hello ----二生三,完全一樣
        hello = Hello()
        # 使用hello調(diào)用方法say_hello ----三生萬(wàn)物,完全一樣
        hello.say_hello()
        輸出效果:
        Hello, world. ----調(diào)用結(jié)果完全一樣。
        我們回頭看一眼最精彩的地方,道直接生出了二:
        Hello = type('Hello’, (object,), dict(say_hello=fn))
        這就是“道”,python世界的起源,你可以為此而驚嘆。
        注意它的三個(gè)參數(shù)!暗合人類的三大永恒命題:我是誰(shuí),我從哪里來(lái),我要到哪里去。
        第一個(gè)參數(shù):我是誰(shuí)。 在這里,我需要一個(gè)區(qū)分于其它一切的命名,以上的實(shí)例將我命名為“Hello”
        第二個(gè)參數(shù):我從哪里來(lái)。在這里,我需要知道從哪里來(lái),也就是我的“父類”,以上實(shí)例中我的父類是“object”——python中一種非常初級(jí)的類。
        第三個(gè)參數(shù):我要到哪里去。在這里,我們將需要調(diào)用的方法和屬性包含到一個(gè)字典里,再作為參數(shù)傳入。以上實(shí)例中,我們有一個(gè)say_hello方法包裝進(jìn)了字典中。
        值得注意的是,三大永恒命題,是一切類,一切實(shí)例,甚至一切實(shí)例屬性與方法都具有的。理所應(yīng)當(dāng),它們的“創(chuàng)造者”,道和一,即type和元類,也具有這三個(gè)參數(shù)。但平常,類的三大永恒命題并不作為參數(shù)傳入,而是以如下方式傳入
        class Hello(object){
        # class 后聲明“我是誰(shuí)”
        # 小括號(hào)內(nèi)聲明“我來(lái)自哪里”
        # 中括號(hào)內(nèi)聲明“我要到哪里去”
        def say_hello(){
        }
        }
        造物主,可以直接創(chuàng)造單個(gè)的人,但這是一件苦役。造物主會(huì)先創(chuàng)造“人”這一物種,再批量創(chuàng)造具體的個(gè)人。并將三大永恒命題,一直傳遞下去。
        “道”可以直接生出“二”,但它會(huì)先生出“一”,再批量地制造“二”。
        type可以直接生成類(class),但也可以先生成元類(metaclass),再使用元類批量定制類(class)。
        元類——道生一,一生二
        一般來(lái)說(shuō),元類均被命名后綴為Metalass。想象一下,我們需要一個(gè)可以自動(dòng)打招呼的元類,它里面的類方法呢,有時(shí)需要say_Hello,有時(shí)需要say_Hi,有時(shí)又需要say_Sayolala,有時(shí)需要say_Nihao。
        如果每個(gè)內(nèi)置的say_xxx都需要在類里面聲明一次,那將是多么可怕的苦役! 不如使用元類來(lái)解決問(wèn)題。
        以下是創(chuàng)建一個(gè)專門“打招呼”用的元類代碼:
        class SayMetaClass(type):
        def __new__(cls, name, bases, attrs):
        attrs['say_' name] = lambda self,value,saying=name: print(saying ',' value '!')
        return type.__new__(cls, name, bases, attrs)
        記住兩點(diǎn):
        元類是由“type”衍生而出,所以父類需要傳入type?!镜郎?,所以一必須包含道】
        元類的操作都在 __new__中完成,它的第一個(gè)參數(shù)是將創(chuàng)建的類,之后的參數(shù)即是三大永恒命題:我是誰(shuí),我從哪里來(lái),我將到哪里去。 它返回的對(duì)象也是三大永恒命題,接下來(lái),這三個(gè)參數(shù)將一直陪伴我們。
        在__new__中,我只進(jìn)行了一個(gè)操作,就是
        attrs['say_' name] = lambda self,value,saying=name: print(saying ',' value '!')
        它跟據(jù)類的名字,創(chuàng)建了一個(gè)類方法。比如我們由元類創(chuàng)建的類叫“Hello”,那創(chuàng)建時(shí)就自動(dòng)有了一個(gè)叫“say_Hello”的類方法,然后又將類的名字“Hello”作為默認(rèn)參數(shù)saying,傳到了方法里面。然后把hello方法調(diào)用時(shí)的傳參作為value傳進(jìn)去,最終打印出來(lái)。
        那么,一個(gè)元類是怎么從創(chuàng)建到調(diào)用的呢?
        來(lái)!一起根據(jù)道生一、一生二、二生三、三生萬(wàn)物的準(zhǔn)則,走進(jìn)元類的生命周期吧!
        # 道生一:傳入type
        class SayMetaClass(type):
        # 傳入三大永恒命題:類名稱、父類、屬性
        def __new__(cls, name, bases, attrs):
        # 創(chuàng)造“天賦”
        attrs['say_' name] = lambda self,value,saying=name: print(saying ',' value '!')
        # 傳承三大永恒命題:類名稱、父類、屬性
        return type.__new__(cls, name, bases, attrs)
        # 一生二:創(chuàng)建類
        class Hello(object, metaclass=SayMetaClass):
        pass
        # 二生三:創(chuàng)建實(shí)列
        hello = Hello()
        # 三生萬(wàn)物:調(diào)用實(shí)例方法
        hello.say_Hello('world!')
        輸出為
        Hello, world!
        注意:通過(guò)元類創(chuàng)建的類,第一個(gè)參數(shù)是父類,第二個(gè)參數(shù)是metaclass
        普通人出生都不會(huì)說(shuō)話,但有的人出生就會(huì)打招呼說(shuō)“Hello”,“你好”,“sayolala”,這就是天賦的力量。它會(huì)給我們面向?qū)ο蟮木幊淌∠聼o(wú)數(shù)的麻煩。
        現(xiàn)在,保持元類不變,我們還可以繼續(xù)創(chuàng)建Sayolala, Nihao類,如下:
        # 一生二:創(chuàng)建類
        class Sayolala(object, metaclass=SayMetaClass):
        pass
        # 二生三:創(chuàng)建實(shí)列
        s = Sayolala()
        # 三生萬(wàn)物:調(diào)用實(shí)例方法
        s.say_Sayolala('japan!')
        輸出
        Sayolala, japan!
        也可以說(shuō)中文
        # 一生二:創(chuàng)建類
        class Nihao(object, metaclass=SayMetaClass):
        pass
        # 二生三:創(chuàng)建實(shí)列
        n = Nihao()
        # 三生萬(wàn)物:調(diào)用實(shí)例方法
        n.say_Nihao('中華!')
        輸出
        Nihao, 中華!
        再來(lái)一個(gè)小例子:
        # 道生一
        class ListMetaclass(type):
        def __new__(cls, name, bases, attrs):
        # 天賦:通過(guò)add方法將值綁定
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)
        # 一生二
        class MyList(list, metaclass=ListMetaclass):
        pass
        # 二生三
        L = MyList()
        # 三生萬(wàn)物
        L.add(1)
        現(xiàn)在我們打印一下L
        print(L)
        >>> [1]
        而普通的list沒有add()方法
        L2 = list()
        L2.add(1)
        >>>AttributeError: 'list' object has no attribute 'add'
        太棒了!學(xué)到這里,你是不是已經(jīng)體驗(yàn)到了造物主的樂趣?
        python世界的一切,盡在掌握。
        年輕的造物主,請(qǐng)隨我一起開創(chuàng)新世界。
        我們選擇兩個(gè)領(lǐng)域,一個(gè)是Django的核心思想,“Object Relational Mapping”,即對(duì)象-關(guān)系映射,簡(jiǎn)稱ORM。
        這是Django的一大難點(diǎn),但學(xué)完了元類,一切變得清晰。你對(duì)Django的理解將更上一層樓!
        另一個(gè)領(lǐng)域是爬蟲領(lǐng)域(黑客領(lǐng)域),一個(gè)自動(dòng)搜索網(wǎng)絡(luò)上的可用代理,然后換著IP去突破別的人反爬蟲限制。
        這兩項(xiàng)技能非常有用,也非常好玩!
        挑戰(zhàn)一:通過(guò)元類創(chuàng)建ORM
        準(zhǔn)備工作,創(chuàng)建一個(gè)Field類
        class Field(object):
        def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type
        def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)
        它的作用是
        在Field類實(shí)例化時(shí)將得到兩個(gè)參數(shù),name和column_type,它們將被綁定為Field的私有屬性,如果要將Field轉(zhuǎn)化為字符串時(shí),將返回“Field:XXX” , XXX是傳入的name名稱。
        準(zhǔn)備工作:創(chuàng)建StringField和IntergerField
        class StringField(Field):
        def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')
        class IntegerField(Field):
        def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')
        它的作用是
        在StringField,IntegerField實(shí)例初始化時(shí),時(shí)自動(dòng)調(diào)用父類的初始化方式。
        道生一
        class ModelMetaclass(type):
        def __new__(cls, name, bases, attrs):
        if name=='Model':
        return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
        if isinstance(v, Field):
        print('Found mapping: %s ==> %s' % (k, v))
        mappings[k] = v
        for k in mappings.keys():
        attrs.pop(k)
        attrs['__mappings__'] = mappings # 保存屬性和列的映射關(guān)系
        attrs['__table__'] = name # 假設(shè)表名和類名一致
        return type.__new__(cls, name, bases, attrs)
        它做了以下幾件事
        創(chuàng)建一個(gè)新的字典mapping
        將每一個(gè)類的屬性,通過(guò).items()遍歷其鍵值對(duì)。如果值是Field類,則打印鍵值,并將這一對(duì)鍵值綁定到mapping字典上。
        將剛剛傳入值為Field類的屬性刪除。
        創(chuàng)建一個(gè)專門的__mappings__屬性,保存字典mapping。
        創(chuàng)建一個(gè)專門的__table__屬性,保存?zhèn)魅氲念惖拿Q。
        一生二
        class Model(dict, metaclass=ModelMetaclass):
        def __init__(self, **kwarg):
        super(Model, self).__init__(**kwarg)
        def __getattr__(self, key):
        try:
        return self[key]
        except KeyError:
        raise AttributeError(''Model' object has no attribute '%s'' % key)
        def __setattr__(self, key, value):
        self[key] = value
        # 模擬建表操作
        def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
        fields.append(v.name)
        args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))
        如果從Model創(chuàng)建一個(gè)子類User:
        class User(Model):
        # 定義類的屬性到列的映射:
        id = IntegerField('id')
        name = StringField('username')
        email = StringField('email')
        password = StringField('password')
        這時(shí)
        id= IntegerField('id’)就會(huì)自動(dòng)解析為:
        Model.__setattr__(self, 'id’, IntegerField('id’))
        因?yàn)镮ntergerField('id’)是Field的子類的實(shí)例,自動(dòng)觸發(fā)元類的__new__,所以將IntergerField('id’)存入__mappings__并刪除這個(gè)鍵值對(duì)。
        二生三、三生萬(wàn)物
        當(dāng)你初始化一個(gè)實(shí)例的時(shí)候并調(diào)用save()方法時(shí)候
        u = User(id=12345, name='Batman', email='batman@nasa.org', password='iamback')
        u.save()
        這時(shí)先完成了二生三的過(guò)程:
        先調(diào)用Model.__setattr__,將鍵值載入私有對(duì)象
        然后調(diào)用元類的“天賦”,ModelMetaclass.__new__,將Model中的私有對(duì)象,只要是Field的實(shí)例,都自動(dòng)存入u.__mappings__。
        接下來(lái)完成了三生萬(wàn)物的過(guò)程:
        通過(guò)u.save()模擬數(shù)據(jù)庫(kù)存入操作。這里我們僅僅做了一下遍歷__mappings__操作,虛擬了sql并打印,在現(xiàn)實(shí)情況下是通過(guò)輸入sql語(yǔ)句與數(shù)據(jù)庫(kù)來(lái)運(yùn)行。
        輸出結(jié)果為
        Found model: User
        Found mapping: name ==>
        Found mapping: password ==>
        Found mapping: id ==>
        Found mapping: email ==>
        SQL: insert into User (username,password,id,email) values (Batman,iamback,12345,batman@nasa.org)
        ARGS: ['Batman', 'iamback', 12345, 'batman@nasa.org']
        年輕的造物主,你已經(jīng)和我一起體驗(yàn)了由“道”演化“萬(wàn)物”的偉大歷程,這也是Django中的Model版塊核心原理。
        接下來(lái),請(qǐng)和我一起進(jìn)行更好玩的爬蟲實(shí)戰(zhàn)(嗯,你現(xiàn)在已經(jīng)是初級(jí)黑客了):網(wǎng)絡(luò)代理的爬取吧!
        挑戰(zhàn)二:網(wǎng)絡(luò)代理的爬取
        準(zhǔn)備工作,先爬個(gè)頁(yè)面玩玩
        請(qǐng)確保已安裝requests和pyquery這兩個(gè)包。
        # 文件:get_page.py
        import requests
        base_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'zh-CN,zh;q=0.8'
        }
        def get_page(url):
        headers = dict(base_headers)
        print('Getting', url)
        try:
        r = requests.get(url, headers=headers)
        print('Getting result', url, r.status_code)
        if r.status_code == 200:
        return r.text
        except ConnectionError:
        print('Crawling Failed', url)
        return None
        這里,我們利用request包,把百度的源碼爬了出來(lái)。
        試一試抓百度
        把這一段粘在get_page.py后面,試完刪除
        if(__name__ == '__main__'):
        rs = get_page('https://www.baidu.com')
        print('result:', rs)
        試一試抓代理
        把這一段粘在get_page.py后面,試完刪除
        if(__name__ == '__main__'):
        from pyquery import PyQuery as pq
        start_url = 'http://www./Region/China'
        print('Crawling', start_url)
        html = get_page(start_url)
        if html:
        doc = pq(html)
        lines = doc('div[name='list_proxy_ip']').items()
        for line in lines:
        ip = line.find('.tbBottomLine:nth-child(1)').text()
        port = line.find('.tbBottomLine:nth-child(2)').text()
        print(ip ':' port)
        接下來(lái)進(jìn)入正題:使用元類批量抓取代理
        批量處理抓取代理
        from getpage import get_page
        from pyquery import PyQuery as pq
        # 道生一:創(chuàng)建抽取代理的metaclass
        class ProxyMetaclass(type):
        '''
        元類,在FreeProxyGetter類中加入
        __CrawlFunc__和__CrawlFuncCount__
        兩個(gè)參數(shù),分別表示爬蟲函數(shù),和爬蟲函數(shù)的數(shù)量。
        '''
        def __new__(cls, name, bases, attrs):
        count = 0
        attrs['__CrawlFunc__'] = []
        attrs['__CrawlName__'] = []
        for k, v in attrs.items():
        if 'crawl_' in k:
        attrs['__CrawlName__'].append(k)
        attrs['__CrawlFunc__'].append(v)
        count = 1
        for k in attrs['__CrawlName__']:
        attrs.pop(k)
        attrs['__CrawlFuncCount__'] = count
        return type.__new__(cls, name, bases, attrs)
        # 一生二:創(chuàng)建代理獲取類
        class ProxyGetter(object, metaclass=ProxyMetaclass):
        def get_raw_proxies(self, site):
        proxies = []
        print('Site', site)
        for func in self.__CrawlFunc__:
        if func.__name__==site:
        this_page_proxies = func(self)
        for proxy in this_page_proxies:
        print('Getting', proxy, 'from', site)
        proxies.append(proxy)
        return proxies
        def crawl_daili66(self, page_count=4):
        start_url = 'http://www./{}.html'
        urls = [start_url.format(page) for page in range(1, page_count 1)]
        for url in urls:
        print('Crawling', url)
        html = get_page(url)
        if html:
        doc = pq(html)
        trs = doc('.containerbox table tr:gt(0)').items()
        for tr in trs:
        ip = tr.find('td:nth-child(1)').text()
        port = tr.find('td:nth-child(2)').text()
        yield ':'.join([ip, port])
        def crawl_proxy360(self):
        start_url = 'http://www./Region/China'
        print('Crawling', start_url)
        html = get_page(start_url)
        if html:
        doc = pq(html)
        lines = doc('div[name='list_proxy_ip']').items()
        for line in lines:
        ip = line.find('.tbBottomLine:nth-child(1)').text()
        port = line.find('.tbBottomLine:nth-child(2)').text()
        yield ':'.join([ip, port])
        def crawl_goubanjia(self):
        start_url = 'http://www./free/gngn/index.shtml'
        html = get_page(start_url)
        if html:
        doc = pq(html)
        tds = doc('td.ip').items()
        for td in tds:
        td.find('p').remove()
        yield td.text().replace(' ', '')
        if __name__ == '__main__':
        # 二生三:實(shí)例化ProxyGetter
        crawler = ProxyGetter()
        print(crawler.__CrawlName__)
        # 三生萬(wàn)物
        for site_label in range(crawler.__CrawlFuncCount__):
        site = crawler.__CrawlName__[site_label]
        myProxies = crawler.get_raw_proxies(site)
        道生一:元類的__new__中,做了四件事:
        將“crawl_”開頭的類方法的名稱推入ProxyGetter.__CrawlName__
        將“crawl_”開頭的類方法的本身推入ProxyGetter.__CrawlFunc__
        計(jì)算符合“crawl_”開頭的類方法個(gè)數(shù)
        刪除所有符合“crawl_”開頭的類方法
        怎么樣?是不是和之前創(chuàng)建ORM的__mappings__過(guò)程極為相似?
        一生二:類里面定義了使用pyquery抓取頁(yè)面元素的方法
        分別從三個(gè)免費(fèi)代理網(wǎng)站抓取了頁(yè)面上顯示的全部代理。
        如果對(duì)yield用法不熟悉,可以查看:廖雪峰的python教程:生成器
        二生三:創(chuàng)建實(shí)例對(duì)象crawler
        略
        三生萬(wàn)物:遍歷每一個(gè)__CrawlFunc__
        在ProxyGetter.__CrawlName__上面,獲取可以抓取的的網(wǎng)址名。
        觸發(fā)類方法ProxyGetter.get_raw_proxies(site)
        遍歷ProxyGetter.__CrawlFunc__,如果方法名和網(wǎng)址名稱相同的,則執(zhí)行這一個(gè)方法
        把每個(gè)網(wǎng)址獲取到的代理整合成數(shù)組輸出。
        那么。。。怎么利用批量代理,沖擊別人的網(wǎng)站,套取別人的密碼,狂發(fā)廣告水貼,定時(shí)騷擾客戶? 呃!想啥呢!這些自己悟!如果悟不到,請(qǐng)聽下回分解!
        年輕的造物主,創(chuàng)造世界的工具已經(jīng)在你手上,請(qǐng)你將它的威力發(fā)揮到極致!
        請(qǐng)記住揮動(dòng)工具的口訣:
        道生一,一生二,二生三,三生萬(wàn)物
        我是誰(shuí),我來(lái)自哪里,我要到哪里去

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多