研究:伊波拉疫情將隨二氧化碳濃度升高而擴大

環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:Carbon Brief

根據發表在《自然通訊(Nature Communication)》的,當大氣中溫室氣體濃度升高,非洲伊波拉疫情爆發的威脅也將隨之增加。

隨著氣溫上升,蝙蝠和其他會將病毒傳播給人類的動物可能遷移到新的區域,進而帶來疾病。

新的模型顯示,如果人口繼續快速增加但發展緩慢且沒有相應的氣候行動,那麼到2070年,伊波拉疫情平均每10年就會爆發一次,今日的平均值是每17年一次。目前還不是疫區的西非和中非也可能受影響。

該研究的結論是,在當前的經濟增長速度和高碳排放水準下,高風險區域的總面積可能擴大五分之一。在更高的碳排放水準下,甚至可能擴大1/3。

作者們說,他們於2018年建立統計模型,成功預測了剛果民主共和國當前的疫情,目前伊波拉在當地已經奪走了兩千多條生命。

因此,他們認為,這份研究當為非洲(包括過去認為未受影響的地區)針對性的伊波拉疫苗計畫和醫療基礎設施的部署奠定基礎。

獅子山共和國境內努力對抗伊波拉病毒蔓延的村民。 (CC BY-NC-ND 2.0)

伊波拉病毒於1976年首次被發現,2014年爆發疫情,造成西非成千上萬人喪生,在世界各地成為頭條新聞。

一般認為這種傳染病是透過所謂的「溢出物感染」從蝙蝠和其他動物宿主傳播給人類的,是一種人畜共通傳染病。所有人類會感染的傳染病中,有2/3是人畜共通傳染病。

伊波拉病毒一旦感染人類,就可經由直接接觸在人與人之間傳播。人類的症狀包括發燒、嘔吐,有時還包括內部和外部出血。平均死亡率約為50%。

由於伊波拉疫情可能造成重大傷亡,科學家們必須了解下一次伊波拉疫情可能在何時何地爆發,以便分配醫療資源。

但是,由於伊波拉自發現以來,確認的疫情暴發僅有23次,很難用傳統預測常見傳染病(如流感)的方法預測。

伊波拉防疫措施。來源: 。(CC BY 2.0)

倫敦大學學院的雷丁(David Redding)博士、瓊斯(Kate Jones)博士及其團隊沒有用以前的爆發資料進行研究,而是「由下而上」建立伊波拉爆發的風險模型。

他們使用的資料包含許多因子,包括寄主分布、人口數、陸上交通、空中交通、以及土地利用類型。

該分析的一個關鍵要素是氣候變遷,它既能影響當地社會經濟發展,也可以影響疾病傳播,而且正如雷丁所說,「在我們的模型中,大部分氣候的影響來自於為攜帶疾病的物種提供更好的條件,從而擴大了其地理分布範圍。」

儘管究竟哪些動物能將伊波拉病毒傳給人類仍存在一些不確定性,最主要的嫌犯是大型狐蝠,而這種狐蝠常常被人類獵來吃。這些動物喜歡溫暖和潮濕的棲息地,隨著氣候變遷,高風險地區的棲息地還可能擴張。

分析中還納入了其他可能是傳染途徑的動物,如猿類和麂羚。

研究人員指出,從他們觀察到的結果看來,氣候的作用不如貧窮(後者與醫療保健的反應密切相關)和人口數。

但他們也發現,溢出物感染隨著溫室氣體濃度的增加而增加。雷丁說,「在我們的模型中,負面影響隨著排放增加越來越明顯。」

研究人員開發出一個可模擬西非流行的薩伊伊波拉病毒(Zaire Ebola virus, EBOV)人傳人和動物傳人的模型。

科學家用圖示說明在不同排放情境下的伊波拉病毒擴散風險。來源:

為了評估複雜的社會、經濟和氣候因素對非洲未來伊波拉病毒傳播的影響,研究團隊將氣候變遷情境中的代表濃度途徑(representative concentration pathways, RCPs)和共享社會經濟途徑(shared socio-economic pathways, SSPs)納入其模型。

RCPs代表不同程度的氣候行動導致溫室氣體濃度不斷升高的情境。SSP代表各種社會經濟發展情境,涉及全球社會、人口統計和經濟學的不同狀況。

接著,研究人員使用這些模型來預測到2070年非洲不同地區的伊波拉疫情風險變化。在大多數情境中,模擬結果顯示伊波拉發病率隨時間持續增加。

但是,在永續發展和氣候變遷大規模緩解的情境,伊波拉疫情風險普遍下降。

在現況條件下,模型預測,由溢出物感染引發的流行病大約每17年發生一次。流行病爆發在此定義為規模在1,500名患者以上的感染狀況。

團隊使用現況條件進行了約1,500次年度模擬,發現其中約有5.8%發生了疫情爆發。接著使用不同的條件組合重複此過程。

最大的增長發生在高排放、高人口增長和經濟發展緩慢的情況下(RCP6.0和SSP3),疫情幾乎每10年爆發一次。(論文的原始摘要寫道,在此情況下,疫情爆發的可能性比今日高出四倍,但是正確應是1.6倍。Carbon Brief與作者聯繫後確認了這一點。)

在最樂觀的情境下(假定排放量適中且發展迅速,即RCP4.5和SSP1),頻率下降到大約每30年一次。

模型也顯示,影響超過200萬人的「災難性疫情爆發」模式也很類似。目前這種事件預估每43年發生一次,但根據模擬,在高排放情境下,它們的發生頻率也將增加。

模型還預測有爆發風險的區域會擴大。例如,在最樂觀的社會經濟和氣候情況下,有爆發風險的總面積與今天相比減少了近一半(分別是40萬平方公里和80萬平方公里)。

相比之下,在低度氣候變遷緩解和中度發展(RCP6.0和SSP2)情境下,該面積增加了20.5%;在更極端的情況(RCP8.5和SSP3)下,該面積進一步增加了34%。

值得注意的是,許多傳染病是透過環境傳播(例如水或土壤傳播),但伊波拉病毒是透過宿主之間的直接接觸傳播。這表示儘管蝙蝠和人類直接受到氣候變遷的影響,但病毒本身卻不太容易受氣候變遷影響。

該論文的結論是,因應氣候變遷的全球性承諾也許有助降低伊波拉病例。但是對於這種推測不應太樂觀,因為「證據顯示不太可能發生全面性的改變」。

「在中部和西部非洲減少貧困以及增加醫療資源,似乎是降低全球未來伊波拉疫情風險最現實的方法。」

除了準確地找出已知有伊波拉疫情的地區外,該模型還確定了尼日、迦納和肯亞等國家都很可能受小規模溢出物感染和伊波拉疫情影響,儘管這些國家從來沒傳出伊波拉疫情過。

Ebola epidemics will ‘increase with greenhouse gas concentrations’, study finds by Josh Gabbatiss

The threat of Ebola outbreaks across Africa will increase as levels of greenhouse gases in the atmosphere rise, according to new research.

With warming temperatures, bats and other animals that are thought to transmit the virus to humans are expected to move into new areas, bringing the disease with them. 

The new modelling suggests that by 2070 epidemics could break out, on average, once every 10 years, if rapid population growth and slow development are accompanied by inaction on climate change. Under today’s conditions, the average is once every 17 years.

According to the analysis, published in , changing conditions may also affect regions of West and Central Africa that are not currently considered at risk.

The paper concludes that with current rates of economic growth and high emissions, the total epidemic-prone area could expand by a fifth. At even higher levels of emissions, it could expand by a third.

The scientists behind the work say their modelling, which was undertaken in 2018, has already successfully predicted the currently underway in the Democratic Republic of Congo that has claimed more than 2,000 lives.

With this in mind, they say the analysis should lay the groundwork for targeted Ebola vaccine programmes and healthcare infrastructure in Africa, including regions previously thought to be unaffected.

Modelling everything

First identified in 1976, Ebola around the world in 2014 when of epidemic proportions killed thousands of people in West Africa.

The viral disease is thought to pass to humans from bats and other animal hosts in so-called “spillover events”. It is one of the many “zoonotic”, or animal-borne, diseases that make up of all human infectious diseases. 

Once spread to humans, Ebola can be transmitted from person to person through direct contact. in humans include fever, vomiting and sometimes both internal and external bleeding. The average fatality rate is around 50%.

Given its potential to inflict significant harm, it is vital that scientists understand when and where the next outbreak of Ebola is likely to strike so that medical resources can be directed accordingly.

However, as there have only been around 23 confirmed outbreaks since the disease was discovered, traditional methods used to predict common infections, such as flu, are difficult.

Instead of working from previous outbreak data, of , along with his colleague and their team, developed an outbreak risk model “from the bottom-up”. 

They used data on a range of factors including host distribution, human population size, people’s movements by roads and air, and land use.

, who leads the group at the and was not involved in the study, tells Carbon Brief that combining all these factors into a model is “extremely complex”. “Therefore, I am impressed by this work,” he says.

One vital component of the analysis was climate change, which can influence disease spread both by affecting both local socioeconomic development and, as Redding tells Carbon Brief, the host species themselves:

“In our model most of the climate effects are through better conditions for the disease-carrying species, thus increasing their native geographical range.”

While there is still about precisely which animals pass Ebola on to humans, the prime suspects are large fruit bats that are often hunted . These creatures are known to prefer warm and wet habitats, which are expected to expand in the target regions as the climate changes.

Other animals thought to provide alternative routes for infection, such as apes and duiker antelopes, were also considered in the analysis.

The researchers note that climate played a less important role in their observed outcomes than poverty – which is closely tied with healthcare response – and human population size. 

However, they also note that spillover events “increased with greenhouse gas concentrations”. Redding explains:

“There is a positive association in our model results with increasingly more negative impact with higher emission scenarios.”

Epidemic expansion

The researchers developed a model that simulated animal-to-human and human-to-human transmission of Zaire Ebola virus (EBOV), the strain responsible for the West African epidemic.

In order to gauge the impact of complex social, economic and climate factors on future Ebola transmission in Africa, the team then incorporated representative concentration pathways () and shared socio-economic pathways () into their modelling.

RCPs broadly represent scenarios with ever-higher levels of greenhouse gases resulting from different levels of climate action. SSPs are for various socioeconomic development scenarios, involving different outcomes for global society, demographics and economics.

(For more, see Carbon Brief’s recent on RCP8.5 and Carbon Brief’s SSP .)

The researchers then used these models to project changes in Ebola risk across different African regions by the year 2070.

Writing in their paper, they say their simulations suggest a “general, ongoing increase in Ebola incidence over time” for most scenarios.

However, for scenarios involving sustainable development and extensive climate mitigation, there was a general decrease in Ebola risk (the chart below gives an idea of changing risk in different scenario combinations).

Change in future risks of Ebola virus disease under different RCP and SSP scenarios. Each map represents mean change in “per grid cell probability” of an Ebola case from zero (yellow) to −0.06 (dark blue) and 0.06 (red), aggregated at a country level with data from the group’s model for 2070. Source: Redding et al. (2019)

Under current conditions, the models predict an epidemic resulting from a spillover event will occur roughly once every 17 years. An epidemic was defined as an outbreak involving more than 1,500 patients.

To arrive at this conclusion, the team ran around 1,500 yearly simulations using present day conditions and found that epidemics occurred in approximately 5.8% of them. This process was then repeated using different sets of conditions.

The greatest increase occurs in a scenario with high emissions, high population growth and slow economic development (RCP6.0 and SSP3), with epidemics expected to occur nearly once every 10 years.

(The likelihood of epidemics occurring under these conditions compared to present day was incorrectly described as four times more likely, instead of 1.6 times more likely, in the paper’s original abstract. The authors confirmed this after Carbon Brief contacted them about the discrepancy.)

Under the most optimistic of the scenarios they used – which assumes moderate emissions and high development (RCP4.5 and SSP1) – the occurrence drops to roughly once every 30 years.

A similar pattern is expected to play out for “catastrophic epidemics” affecting more than two million people. These events are currently predicted to take place once every 43 years, but the modelling suggests they will increase in frequency under high-emissions scenarios.

The models also predict an expansion in the area at risk from outbreaks. For example, under the most optimistic socioeconomic and climate scenario, the total area where epidemics could start decreased by nearly half compared to present day (0.4m square kilometres, km2, compared to 0.8m km2).

In comparison, in a scenario involving low climate mitigation and “middle of the road” development (RCP6.0 and SSP2), this area increased by 20.5%, Under a more extreme scenario (RCP8.5 and SSP3) this increased further, by 34%.

Disease and climate

While the links between climate and Ebola, Redding and his co-authors think this is the first attempt to model future transmission of the disease that takes climate change into account.

Baylis says there has been extensive work exploring direct links between changing climate and the spread of disease – for example, in the Kenyan highlands and malaria risk. 

However, while work from his own group has suggested of infectious diseases are susceptible to climate, he says identifying these links can be challenging:

“Attribution is difficult, because we are really saying that climate change increases the probability of an event, but extreme events are expected even without climate change.”

It is worth noting that while many infectious diseases are transmitted via the environment (by way of water or soil, for example), Ebola moves via direct contact between hosts. This means while bats and humans are affected directly by the changing climate, the virus itself is less susceptible to a changing climate.

The paper concludes that global commitments to tackle climate change may drive down Ebola cases. However, it is not optimistic about this outcome, noting “evidence suggests a wholesale change is unlikely”:

“Efforts to decrease poverty in Central and Western Africa with a concomitant increase in healthcare resources, therefore, would appear to be the most realistic approach to reducing future Ebola virus disease risk globally.”

Future response

Besides accurately identifying the known endemic area for Ebola, the modelling also identifies countries such as Nigeria, Ghana and Kenya as vulnerable to both small spillovers and epidemics of Ebola, despite never having been the source of such outbreaks before.

The team says their approach “somewhat contradict[s] analyses based on current case data”.

According to Baylis, the team’s validation of past outbreaks using the new approach increases confidence in future predictions, but notes the models are still “likely constrained by the quality of our knowledge of Ebola hosts, which is not high”.

Besides identifying a “much larger” area within Africa that is vulnerable to spillover events, the team also incorporated information about airline routes and concluded that China, Russia, India, the US and many European countries were at risk of importing the disease.

According to Redding, their work “acts as a call” for both a better understanding of where Ebola outbreaks could hit, plus the need for cooperation between wealthier and poorer countries to improve healthcare resources in preparation. He concludes:

“Such an approach is a no-lose situation as better containment facilities and barrier nursing, for example, could protect nations and their neighbours against many different future disease outbreaks, not just Ebola.”

※ 全文及圖片詳見:()

※ 論文資料:Redding, D. et al. (2019) Impacts of environmental and socio-economic factors on emergence and epidemic potential of Ebola in Africa, Nature Communications,

作者

如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

用剛果童工挖鈷致死或殘 5科技巨頭挨告

摘錄自2019年12月17日聯合報報導

特斯拉、蘋果、微軟、戴爾、Google母公司字母(Alphabet)以共謀罪名被告上法庭。這是科技業首次因其鈷來源,共同面臨法律訴訟。

代表剛果民主共和國14個家庭擔任原告的美國人權組織「國際權利倡議」(International Rights Advocates)15日提告五家全球大型科技公司,指強迫勞動的體系導致這些家庭的小孩死亡或重傷,而五家科技業者是這個體系的一部分。

這起訴訟說,案件裡的兒童,最小的才6歲,因為出身赤貧家庭,不得不休學到嘉能可的礦坑挖鈷。他們每周要工作六天,有些人領的日薪低到只有1.5美元(約台幣45元)。

鈷是製造科技產品內部可重複使用鋰電池的必要材料。全球一半以上的鈷產自剛果民主共和國。根據歐盟執委會2018年的一份研究,未來10年全球鈷需求料將每年增加7%到13%。原告主張,挨告的科技業者全都有能力徹底整頓旗下鈷供應鏈,以確保更安全的工作條件。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

跟殺蟑盒一樣的殺蜂盒 日網友:不分蜂類都傷害到怎麼辦呢?

文:宋瑞文

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

【K8S】Service服務詳解,看這一篇就夠了!!

k8s用命名空間namespace把資源進行隔離,默認情況下,相同的命名空間里的服務可以相互通訊,反之進行隔離。

1.1 Service

Kubernetes中一個應用服務會有一個或多個實例(Pod,Pod可以通過rs進行多複本的建立),每個實例(Pod)的IP地址由網絡插件動態隨機分配(Pod重啟后IP地址會改變)。為屏蔽這些後端實例的動態變化和對多實例的負載均衡,引入了Service這個資源對象,如下所示:

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx
spec:
  type: ClusterIP
  ports:
    - port: 80
       targetPort: 80
  selector:  #service通過selector和pod建立關聯
    app: nginx

根據創建Service的type類型不同,可分成4種模式:

  • ClusterIP: 默認方式。根據是否生成ClusterIP又可分為普通Service和Headless Service兩類:
    • 普通Service:通過為Kubernetes的Service分配一個集群內部可訪問的固定虛擬IP(Cluster IP),實現集群內的訪問。為最常見的方式。
    • Headless Service:該服務不會分配Cluster IP,也不通過kube-proxy做反向代理和負載均衡。而是通過DNS提供穩定的絡ID來訪問,DNS會將headless service的後端直接解析為podIP列表。主要供StatefulSet使用。
  • NodePort:除了使用Cluster IP之外,還通過將service的port映射到集群內每個節點的相同一個端口,實現通過nodeIP:nodePort從集群外訪問服務。
  • LoadBalancer:和nodePort類似,不過除了使用一個Cluster IP和nodePort之外,還會向所使用的公有雲申請一個負載均衡器(負載均衡器後端映射到各節點的nodePort),實現從集群外通過LB訪問服務。
  • ExternalName:是 Service 的特例。此模式主要面向運行在集群外部的服務,通過它可以將外部服務映射進k8s集群,且具備k8s內服務的一些特徵(如具備namespace等屬性),來為集群內部提供服務。此模式要求kube-dns的版本為1.7或以上。這種模式和前三種模式(除headless service)最大的不同是重定向依賴的是dns層次,而不是通過kube-proxy。
    比如,在service定義中指定externalName的值”my.database.example.com”:

此時k8s集群內的DNS服務會給集群內的服務名 ..svc.cluster.local 創建一個CNAME記錄,其值為指定的”my.database.example.com”。
當查詢k8s集群內的服務my-service.prod.svc.cluster.local時,集群的 DNS 服務將返回映射的CNAME記錄”foo.bar.example.com”。

備註: 前3種模式,定義服務的時候通過selector指定服務對應的pods,根據pods的地址創建出endpoints作為服務後端;Endpoints Controller會watch Service以及pod的變化,維護對應的Endpoint信息。kube-proxy根據Service和Endpoint來維護本地的路由規則。當Endpoint發生變化,即Service以及關聯的pod發生變化,kube-proxy都會在每個節點上更新iptables,實現一層負載均衡。 而ExternalName模式則不指定selector,相應的也就沒有port和endpoints。 ExternalName和ClusterIP中的Headles Service同屬於Headless Service的兩種情況。Headless Service主要是指不分配Service IP,且不通過kube-proxy做反向代理和負載均衡的服務。

1.2 Port

Service中主要涉及三種Port: * port 這裏的port表示service暴露在clusterIP上的端口,clusterIP:Port 是提供給集群內部訪問kubernetes服務的入口。

  • targetPort

containerPort,targetPort是pod上的端口,從port和nodePort上到來的數據最終經過kube-proxy流入到後端pod的targetPort上進入容器。

  • nodePort

nodeIP:nodePort 是提供給從集群外部訪問kubernetes服務的入口。

總的來說,port和nodePort都是service的端口,前者暴露給從集群內訪問服務,後者暴露給從集群外訪問服務。從這兩個端口到來的數據都需要經過反向代理kube-proxy流入後端具體pod的targetPort,從而進入到pod上的容器內。

1.3 IP

使用Service服務還會涉及到幾種IP:

  • ClusterIP

Pod IP 地址是實際存在於某個網卡(可以是虛擬設備)上的,但clusterIP就不一樣了,沒有網絡設備承載這個地址。它是一個虛擬地址,由kube-proxy使用iptables規則重新定向到其本地端口,再均衡到後端Pod。當kube-proxy發現一個新的service后,它會在本地節點打開一個任意端口,創建相應的iptables規則,重定向服務的clusterIP和port到這個新建的端口,開始接受到達這個服務的連接。

  • Pod IP

Pod的IP,每個Pod啟動時,會自動創建一個鏡像為gcr.io/google_containers/pause的容器,Pod內部其他容器的網絡模式使用container模式,並指定為pause容器的ID,即:network_mode: “container:pause容器ID”,使得Pod內所有容器共享pause容器的網絡,與外部的通信經由此容器代理,pause容器的IP也可以稱為Pod IP。

  • 節點IP

Node-IP,service對象在Cluster IP range池中分配到的IP只能在內部訪問,如果服務作為一個應用程序內部的層次,還是很合適的。如果這個service作為前端服務,準備為集群外的客戶提供業務,我們就需要給這個服務提供公共IP了。指定service的spec.type=NodePort,這個類型的service,系統會給它在集群的各個代理節點上分配一個節點級別的端口,能訪問到代理節點的客戶端都能訪問這個端口,從而訪問到服務。

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

C#中的閉包和意想不到的坑

雖然閉包主要是函數式編程的玩意兒,而C#的最主要特徵是面向對象,但是利用委託或lambda表達式,C#也可以寫出具有函數式編程風味的代碼。同樣的,使用委託或者lambda表達式,也可以在C#中使用閉包。

根據WIKI的定義,閉包又稱語法閉包或函數閉包,是在函數式編程語言中實現語法綁定的一種技術。閉包在實現上是一個結構體,它存儲了一個函數(通常是其入口地址)和一個關聯的環境(相當於一個符號查找表)。閉包也可以延遲變量的生存周期。

嗯。。看定義好像有點迷糊,讓我們看看下面的例子吧

    class Program
    {
        static Action CreateGreeting(string message)
        {
            return () => { Console.WriteLine("Hello " + message); };
        }

        static void Main()
        {
            Action action = CreateGreeting("DeathArthas");
            action();
        }
    }

這個例子非常簡單,用lambda表達式創建一個Action對象,之後再調用這個Action對象。
但是仔細觀察會發現,當Action對象被調用的時候,CreateGreeting方法已經返回了,作為它的實參的message應該已經被銷毀了,那麼為什麼我們在調用Action對象的時候,還是能夠得到正確的結果呢?
 
原來奧秘就在於,這裏形成了閉包。雖然CreateGreeting已經返回了,但是它的局部變量被返回的lambda表達式所捕獲,延遲了其生命周期。怎麼樣,這樣再回頭看閉包定義,是不是更清楚了一些?
 
閉包就是這麼簡單,其實我們經常都在使用,只是有時候我們都不自知而已。比如大家肯定都寫過類似下面的代碼。

void AddControlClickLogger(Control control, string message)
{
	control.Click += delegate
	{
		Console.WriteLine("Control clicked: {0}", message);
	}
}

這裏的代碼其實就用了閉包,因為我們可以肯定,在control被點擊的時候,這個message早就超過了它的聲明周期。合理使用閉包,可以確保我們寫出在空間和時間上面解耦的委託。
 
不過在使用閉包的時候,要注意一個陷阱。因為閉包會延遲局部變量的生命周期,在某些情況下程序產生的結果會和預想的不一樣。讓我們看看下面的例子。

    class Program
    {
	static List<Action> CreateActions()
        {
            var result = new List<Action>();
            for(int i = 0; i < 5; i++)
            {
                result.Add(() => Console.WriteLine(i));
            }
            return result;
        }

        static void Main()
        {
            var actions = CreateActions();
            for(int i = 0;i<actions.Count;i++)
            {
                actions[i]();
            }
        }
    }

這個例子也非常簡單,創建一個Action鏈表並依次執行它們。看看結果

相信很多人看到這個結果的表情是這樣的!!難道不應該是0,1,2,3,4嗎?出了什麼問題?

刨根問底,這兒的問題還是出現在閉包的本質上面,作為“閉包延遲了變量的生命周期”這個硬幣的另外一面,是一個變量可能在不經意間被多個閉包所引用。

在這個例子裏面,局部變量i同時被5個閉包引用,這5個閉包共享i,所以最後他們打印出來的值是一樣的,都是i最後退出循環時候的值5。

要想解決這個問題也很簡單,多聲明一個局部變量,讓各個閉包引用自己的局部變量就可以了。

	//其他都保持與之前一致
        static List<Action> CreateActions()
        {
            var result = new List<Action>();
            for (int i = 0; i < 5; i++)
            {
                int temp = i; //添加局部變量
                result.Add(() => Console.WriteLine(temp));
            }
            return result;
        }

這樣各個閉包引用不同的局部變量,剛剛的問題就解決了。

除此之外,還有一個修復的方法,在創建閉包的時候,使用foreach而不是for。至少在C# 7.0 的版本上面,這個問題已經被注意到了,使用foreach的時候編譯器會自動生成代碼繞過這個閉包陷阱。

	//這樣fix也是可以的
        static List<Action> CreateActions()
        {
            var result = new List<Action>();
            foreach (var i in Enumerable.Range(0,5))
            {
                result.Add(() => Console.WriteLine(i));
            }
            return result;
        }

這就是在閉包在C#中的使用和其使用中的一個小陷阱,希望大家能通過老胡的文章了解到這個知識點並且在開發中少走彎路!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

區塊鏈系列教程之:比特幣的錢包與交易

目錄

  • 簡介
  • 比特幣密碼學的基礎
    • 單向散列函數(hash算法)
    • 非對稱加密算法
    • 擴展閱讀:同態加密
  • 密鑰,地址和錢包
  • 比特幣中的交易
  • 擴展閱讀:圖靈非完備性
  • 總結

簡介

錢包在比特幣中是做什麼的呢?比特幣的交易又有什麼特點呢?怎麼才能偽造比特幣的交易呢?今天和大家一起學習一下比特幣中的錢包和交易。

比特幣密碼學的基礎

之前我們提到過比特幣使用的並不是什麼新技術,只是對於老的技術比如:P2P網絡,分佈式系統,密碼學,共識算法的重新而又巧妙的應用。

在錢包和交易生成驗證的過程中,都需要使用到密碼學的計算。這裏我們先介紹一下比特幣中會使用到的幾種密碼學技術。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

單向散列函數(hash算法)

在介紹單向散列函數之前,我們先了解一下什麼情況下需要使用到單向散列函數。

如果你需要從國外的網站上下載一個軟件,但是因為種種原因,國外的網絡太慢了,下載幾個G的數據幾乎是不可能的。剛好國內有鏡像網站,可以從國內下載數據。但是如何保證國內的鏡像不是被篡改過後的呢?這個時候就需要單向散列函數了。一般來說網站會提供MD5或者SHA的值作為驗證值。

單向散列函數有一個輸入和輸出。輸入稱為消息,輸出稱為散列值。

散列值的長度跟消息的長度無關,不論多少大小的長度的消息,都會計算出固定長度的散列值。

hash算法有下面幾個特點:

  1. 能夠根據任意長度的消息計算出固定長度的散列值。

  2. 計算速度要快。

  3. 消息不同,散列值也不同。

    這就意味着,如果僅僅是一點點的變動都會引起整個散列值的巨大變化。

    因為散列值的大小是固定的,所以有可能會出現不同的消息產生相同散列值的情況。這種情況叫做碰撞。

    難以發現碰撞的性質被稱為抗碰撞性。當給定某條消息的散列值時,必須保證很難找到和該消息具有相同散列值的另一條消息。

  4. 單向散列函數必須具有單向性。所謂單向性是指無法通過散列值來反推出消息的性質。

比特幣使用的散列算法是SHA256,他是安全散列算法SHA(Secure Hash Algorithm)系列算法的一種(另外還有SHA-1、SHA-224、SHA-384 和 SHA-512 等變體),SHA是美國國家安全局 (NSA) 設計,美國國家標準與技術研究院(NIST) 發布的,主要適用於数字簽名標準(DigitalSignature Standard DSS)裏面定義的数字簽名算法(Digital Signature Algorithm DSA)。

RIPEMD(RACE Integrity Primitives Evaluation Message Digest,RACE原始完整性校驗消息摘要),是Hans Dobbertin等3人在md4,md5的基礎上,於1996年提出來的。

非對稱加密算法

非對稱加密算法也叫公鑰密碼算法,通過生成的公私鑰來對明文密文進行加密解密。

非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。

擴展閱讀:同態加密

同態加密是一種加密形式,它允許人們對密文進行特定的代數運算得到仍然是加密的結果,將其解密所得到的結果與對明文進行同樣的運算結果一樣。換言之,這項技術令人們可以在加密的數據中進行諸如檢索、比較等操作,得出正確的結果,而在整個處理過程中無需對數據進行解密。其意義在於,真正從根本上解決將數據及其操作委託給第三方時的保密問題,例如對於各種雲計算的應用。

密鑰,地址和錢包

比特幣的所有權是通過数字密鑰、比特幣地址和数字簽名來確立的。数字密鑰實際上並不是存儲在網絡中,而是由用戶生成並存儲在一個文件或簡單的數據庫 中,稱為錢包。存儲在用戶錢包中的数字密鑰完全獨立於比特幣協議,可由用戶的錢包軟件生成並管理,而無需區塊鏈或網絡連接。密鑰實現了比特幣的許多有趣特性,包括去中心化信任和控制、所有權認證和基於密碼學證明的安全模型。

比特幣錢包只包含私鑰而不是比特幣。每一個用戶有一個包含多個私鑰的錢包。錢包中包含成對的私鑰和公鑰。用戶用這些私鑰來簽名交易,從而證明它們擁有交易的輸出(也就是其中的比特幣)。比特幣是以交易輸出的形式來儲存在區塊鏈中(通常記為vout或txout)。

如果錢包只包含私鑰,那麼錢包地址是什麼呢?錢包地址是從公鑰的hash值的出來的,如下圖所示:

  1. 首先使用隨機數發生器生成一個『私鑰』。一般來說這是一個256bits的數,擁有了這串数字就可以對相應『錢包地址』中的比特幣進行操作,所以必須被安全地保存起來。

  2. 『私鑰』經過SECP256K1算法處理生成了『公鑰』。SECP256K1是一種橢圓曲線算法,通過一個已知『私鑰』時可以算得『公鑰』,而『公鑰』已知時卻無法反向計算出『私鑰』。這是保障比特幣安全的算法基礎。

  3. 同SHA256一樣,RIPEMD160也是一種Hash算法,由『公鑰』可以計算得到『公鑰哈希』,而反過來是行不通的。

  4. 將一個字節的地址版本號連接到『公鑰哈希』頭部(對於比特幣網絡的pubkey地址,這一字節為“0”),然後對其進行兩次SHA256運算,將結果的前4字節作為『公鑰哈希』的校驗值,連接在其尾部。

  5. 將上一步結果使用BASE58進行編碼(比特幣定製版本),就得到了『錢包地址』。 比如,1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa。

所以私鑰,公鑰和錢包地址的關係如下圖所示:

大家看到錢包地址1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa有什麼想法呢?

肯定有人在想,這麼一大長串字母和数字實在是太不好記憶了。能不能生產一個比較好記的錢包地址呢? 比如MyNameIsHanMeiMei….這樣開頭的地址呢?

當然可以,這叫做靚號地址,只不過需要大量的算力才行。

比特幣中的交易

簡單來說,交易就是告知全網:比特幣的持有者已授權把比特幣轉帳給其他人。而新持有者能夠再次授權,轉移給該比特幣所有權鏈中的其他人。

注意, 在比特幣的世界里既沒有賬戶,也沒有餘額,只有分散到區塊鏈里的UTXO(Unspent Transaction Outputs)。

怎麼理解這個UTXO呢?沒有賬戶也沒有餘額,那麼錢包裏面的金額是怎麼計算出來的呢?

別急,讓我們一一道來。

話說,在比特幣中,比特幣錢包間的轉賬是通過交易(Transaction)實現的。

我們看一個標準的交易流程。

那麼問題來了,世界上第一個比特幣是哪裡來的呢?

答,是挖礦來的。好了,我們的001交易表示的就是一個挖礦的過程,在這個交易中,輸入就是挖礦,輸出編號1,BTC數目是50,目的地址是A,表示這50個BTC給A了。

接下來,A想發25個BTC給B,怎麼構造這個交易呢?

同樣的,我們需要一個輸入,這個輸入就是001交易的1號輸出,我們用001.1來表示。輸出分為兩個,第一個輸出編號1,表示要付25個BTC給B。第二個輸出編號2,表示剩下的BTC要還給A。

大家可能會問了,輸入是50BTC,兩個輸出加起來才45個BTC,好像還少了5個BTC?沒錯,這個5個BTC就是給礦工的挖礦所得。

接下來,A又繼續轉賬給C,同樣的道理,把一個一個的交易連接起來。

從上面的例子我們可以看到,實際上錢是存在一個一個的交易記錄裏面的,那些未被花費的輸出,就叫做UTXO(Unspent Transaction Outputs)。

那麼怎麼保證轉賬給B的錢,不會被其他的人消費呢?這就涉及到交易的加密過程了。

我們以單個輸入和輸出為例來詳細了解一下交易的構成:

上圖中,交易的輸入就是txid,也就是之前生成的還有未花費暑輸出的交易ID。output index就是交易的輸出id。

一個非常重要的ScriptSig是輸入交易的驗證,表明這個用戶擁有這個賬戶的轉賬權限。

輸出是一個腳本,只有滿足腳本運行條件的人才能花費這個output。這也就是ScriptSig需要驗證的腳本。

我們看下腳本是怎麼做認證的吧。

比特幣的標準輸出形式有兩種。Pay To Public Key Hash (P2PKH) 和 Pay To Script Hash (P2SH)。兩者的區別在於,一個是輸出到public key的hash,一個是輸出到任意的一個腳本輸出hash。

為了保證輸出只能由特定的人來花費,一般的情況下是直接輸出到對方的public key hash。由於只有對方擁有的私鑰能夠生成這個public key hash,也就是說只有對方才能夠對這個輸出進行驗證。

但每次都需要知道對方的public key hash還是比較麻煩的,更簡單的做法就是,發送者直接輸出到一個特定的hash值就行了,只要對方能夠生成這個hash就可以。

下面的例子是一個P2PKH的腳本形式。

P2PKH的輸出是一個腳本,裏面一個重要的值就是PK hash。

怎麼驗證呢?

驗證方提供兩個值,一個是sig,一個是PubKey。因為比特幣的虛擬機是棧結構的,我們先把這兩個值入棧。

然後調用OP_DUP對最上層的PubKey進行拷貝,然後調用OP_HASH160算法來計算Pk Hash,然後將發送方保存的Pk Hash入棧。接下來調用OP_EQUALVERIFY對兩個PK Hash進行對比。

如果對比成功,最後一步就是驗證Sig和PubKey是否匹配。

如果都成功,說明接收方的確是這個PK Hash的擁有者。那麼對方就可以盡情使用了。

擴展閱讀:圖靈非完備性

和馮·諾伊曼同為現代計算機奠基人的阿蘭·圖靈(AlanTurin)在1950年提出了判定計算機能否像人那般實際“思考”的標準,也就是著名的“圖靈檢驗”。

他設想一台超級計算機和一個人躲藏在幕後回答提問者的問題,而提問者則試圖分辨哪個是人哪個是計算機。

圖靈爭辯說,假如計算機偽裝得如此巧妙,以致沒有人可以在實際上把它和一個真人分辨開來的話,那麼我們就可以聲稱,這台計算機和人一樣具備了思考能力,或者說,意識(他的原詞是“智慧”)。

在可計算性理論里,如果一系列操作數據的規則(如指令集、編程語言、細胞自動機)按照一定的順序可以計算出結果,被稱為圖靈完備(turing complete)。

比特幣腳本語言不是圖靈完備的,具有一定的局限性,它沒有循環語句和複雜的條件控制語句。

總結

本文介紹了比特幣的錢包和交易的概念,希望大家能夠喜歡。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/bitcoin-transactions/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

09.DRF-ModelSerializer

四、模型類序列化器ModelSerializer

如果我們想要使用序列化器對應的是Django的模型類,DRF為我們提供了ModelSerializer模型類序列化器來幫助我們快速創建一個Serializer類。

ModelSerializer與常規的Serializer相同,但提供了:

  • 基於模型類自動生成一系列字段
  • 基於模型類自動為Serializer生成validators,比如unique_together
  • 包含默認的create()和update()的實現

4.1 定義

比如我們創建一個BookInfoSerializer

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'
  • model 指明參照哪個模型類
  • fields 指明為模型類的哪些字段生成

我們可以在python manage.py shell中查看自動生成的BookInfoSerializer的具體實現

>>> from booktest.serializers import BookInfoSerializer
>>> serializer = BookInfoSerializer()
>>> serializer
BookInfoSerializer():
    id = IntegerField(label='ID', read_only=True)
    btitle = CharField(label='名稱', max_length=20)
    bpub_date = DateField(allow_null=True, label='發布日期', required=False)
    bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
    bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=-2147483648, required=False)
    image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)

4.2 指定字段

4.2.1) 使用fields來明確字段,__all__表名包含所有字段,也可以寫明具體哪些字段,如

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date')

4.2.2) 使用exclude可以明確排除掉哪些字段

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        exclude = ('image',)

4.2.3) 默認ModelSerializer使用主鍵作為關聯字段,但是我們可以使用depth來簡單的生成嵌套表示,depth應該是整數,表明嵌套的層級數量。如:

class HeroInfoSerializer2(serializers.ModelSerializer):
    class Meta:
        model = HeroInfo
        fields = '__all__'
        depth = 1

形成的序列化器如下:

HeroInfoSerializer():
    id = IntegerField(label='ID', read_only=True)
    hname = CharField(label='名稱', max_length=20)
    hgender = ChoiceField(choices=((0, 'male'), (1, 'female')), label='性別', required=False, validators=[<django.core.valators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
    hcomment = CharField(allow_null=True, label='描述信息', max_length=200, required=False)
    hbook = NestedSerializer(read_only=True):
        id = IntegerField(label='ID', read_only=True)
        btitle = CharField(label='名稱', max_length=20)
        bpub_date = DateField(allow_null=True, label='發布日期', required=False)
        bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
        bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=-2147483648, required=False)
        image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)

4.2.4) 显示指明字段,如:

class HeroInfoSerializer(serializers.ModelSerializer):
    hbook = BookInfoSerializer()

    class Meta:
        model = HeroInfo
        fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')

4.2.5) 指明只讀字段

可以通過read_only_fields指明只讀字段,即僅用於序列化輸出的字段

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        read_only_fields = ('id', 'bread', 'bcomment')

4.3 添加額外參數

我們可以使用extra_kwargs參數為ModelSerializer添加或修改原有的選項參數

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書數據序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        extra_kwargs = {
            'bread': {'min_value': 0, 'required': True},
            'bcomment': {'min_value': 0, 'required': True},
        }

# BookInfoSerializer():
#    id = IntegerField(label='ID', read_only=True)
#    btitle = CharField(label='名稱', max_length=20)
#    bpub_date = DateField(allow_null=True, label='發布日期', required=False)
#    bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=0, required=True)
#    bcomment = IntegerField(label='評論量', max_value=2147483647, min_value=0, required=True)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

一起玩轉微服務(6)——通信協議如何統一

一、接口調用

接口調用如果是遠程調用,那麼就構成了簡單的分佈式。最簡單的遠程接口實現方式是web service或rest。當然一個合理的分佈式應用不僅僅是遠程接口調用這麼簡單。還需要有負載均衡、緩存等功能。最簡單實現分佈式的技術是Rest接口,因為Rest接口可以使用現存的各種服務器,比如負載均衡服務器和緩存服務器來實現負載均衡和緩存功能。

二、統一通信協議

關於通信協議,不同的公司有不同的選擇,但是建議同一公司內部使用統一的通信協議,比較典型的有grpc和brpc。

1. gRPC簡介

gRPC是Google發布的基於HTTP 2.0傳輸層協議承載的高性能開源軟件框架,提供了支持多種編程語言的、對網絡設備進行配置和納管的方法。由於是開源框架,通信的雙方可以進行二次開發,所以客戶端和服務器端之間的通信會更加專註於業務層面的內容,減少了對由gRPC框架實現的底層通信的關注。如下圖,DATA部分即業務層面內容,下面所有的信息都由gRPC進行封裝。

 

 

grpc是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C、Java 和 Go 語言版本,分別是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
grpc基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP 連接上的多復用請求等特。這些特性使得其在移動設備上表現更好,更省電和節省空間佔用。
關於具體gRPC報文的結構,可以參考下圖:

 

 

下面展示一下gRPC的交互過程

 

 

  1. 交換機在開啟gRPC功能后充當gRPC客戶端的角色,採集服務器充當gRPC服務器角色;
  2. 交換機會根據訂閱的事件構建對應數據的格式(GPB/JSON),通過Protocol Buffers進行編寫proto文件,交換機與服務器建立gRPC通道,通過gRPC協議向服務器發送請求消息;
  3. 服務器收到請求消息后,服務器會通過Protocol Buffers解譯proto文件,還原出最先定義好格式的數據結構,進行業務處理;
  4. 數據梳理完后,服務器需要使用Protocol Buffers重編譯應答數據,通過gRPC協議向交換機發送應答消息;
  5. 交換機收到應答消息后,結束本次的gRPC交互。

上圖展示的是gRPC交互過程的具體流程,這也是Telemetry觸發方式其中之一,稱為Dial-out模式。簡單地說,gRPC就是在客戶端和服務器端開啟gRPC功能后建立連接,將設備上配置的訂閱數據推送給服務器端。

2. brpc

與grpc類似,brpc源自百度,目前支撐百度內部大約 75 萬個同時在線的實例。
其實基於以上的幾種選擇都能夠完成高效的開發,團隊內部使用統一的標準,這樣更有利於模塊化和統一標準。
服務間的通信是通過輕量級的web服務,使用同步的REST API進行通信。在實際的項目應用中,一般推薦在查詢的時候使用同步機制,在增刪改使用異步的方式,結合消息隊列來實現數據的操作,以保證最終的數據一致性。
具體可以使用BRPC做如下

  1. 搭建能在一個端口支持多協議的服務, 或訪問各種服務
  2. Server能同步或異步處理請求
  3. Client支持同步、異步、半同步,或使用組合channels簡化複雜的分庫或併發訪問
  4. 通過http界面調試服務, 使用cpu, heap, contention profilers
  5. 獲得更好的延時和吞吐
  6. 把你組織中使用的協議快速地加入brpc,或定製各類組件, 包括命名服務 (dns, zk, etcd), 負載均衡 (rr, random, consistent hashing)

三、rest API

 

 

REST API 應為創建、檢索、更新和刪除操作使用標準 HTTP 動詞,而且應特別注意操作是否冪等。
POST 操作可用於創建資源。POST 操作的明顯特徵是它不是冪等的。舉例而言,如果使用 POST 請求創建資源,而且啟動該請求多次,那麼每次調用后都會創建一個新的唯一資源。
GET 操作必須是冪等的且不會產生意外結果。具體來講,帶有查詢參數的 GET 請求不應用於更改或更新信息(而應使用 POST、PUT 或 PATCH)。
PUT 操作可用於更新資源。PUT 操作通常包含要更新的資源的完整副本,使該操作具有冪等性。
PATCH 操作允許對資源執行部分更新。它們不一定是冪等的,具體取決於如何指定增量並應用到資源上。例如,如果一個 PATCH 操作表明一個值應從 A 改為 B,那麼它就是冪等的。如果它已啟動多次而且值已是 B,則沒有任何效果。對 PATCH 操作的支持仍不一致。例如,Java EE7 中的 JAX-RS 中沒有 @PATCH 註釋。
DELETE 操作用於刪除資源。刪除操作是冪等的,因為資源只能刪除一次。但是,返回代碼不同,因為第一次操作將成功 (200),而後續調用不會找到資源 (204)。

 

四、你們怎麼解決

不同項目組之間使用的語言有可能不同,框架有可能不同,同樣的,通信協議有可能不同,你們怎麼解決的呢?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

一文梳理JavaScript中的this

最近零零碎碎看了許多關於this的文章,本着“好記性不如爛筆頭”的思想,特在這裏整理一下this有關的知識點。【長文警告!!!】

接下來,筆者將按照以下目錄對this進行闡述:

  • this是什麼?
  • this指向
    • this在全局範圍內
    • this在對象的構造函數內
    • this在對象的方法內
    • this在簡單函數內
    • this在箭頭函數內
    • this在一個事件偵聽器內
  • this綁定規則
    • 默認綁定
    • 隱式綁定
    • 显示綁定(this修改)
    • 優先級
  • 箭頭函數

1. this是什麼?

this是JavaScript的一個關鍵字,但它時常矇著面紗讓人無法捉摸,許多對this不明就裡的同學,常常會有這樣的錯誤認知:

  • this在函數內指向函數自身

    •   function foo(num){
            console.log("foo: " + num);
        
            //記錄foo被調用次數
            this.count++;
        }
        foo.count = 0;
        for(let i=0; i<10; i++){
            if(i > 5){
                foo(i);
            }
        }
        console.log(foo.count); // 0, this並沒有指向foo函數,foo.count沒有進行任何操作
      
  • this在函數內指向函數的作用域

    •   function foo(){
            var a = 2;
            this.bar();
        }
        function bar(){
            console.log(this.a);
        }
        foo();// undefined, window對象沒有bar這一屬性
      

2. this指向

this的指向取決於他所處的環境. 大致上,可以分為下面的6種情況:

  • this在全局範圍內
  • this在對象的構造函數內
  • this在對象的方法內
  • this在一個簡單的函數內
  • this在箭頭函數內
  • this在一個事件偵聽器內

2.1 this在全局範圍內

this在全局範圍內綁定什麼呢?這個相信只要學過JS,應該都知道答案。如果不知道,同學真的應該反思自己的學習態度和方法是否存在問題了。話不多說,直接上代碼,一探究竟,揭開this在全局範圍下的真面目:

console.log(this); // Window

不出意外,this在全局範圍內指向window對象()。通常, 在全局環境中, 我們很少使用this關鍵字, 因此對它也沒那麼在意. 讓我們繼續看下一個環境.

2.2 this在對象的構造函數內

當我們使用new創建構造函數的實例時會發生什麼呢?以這種方式調用構造函數會經歷以下四個步驟:

  • 創建一個空對象;

  • 將構造函數的作用域賦給新對象(this指向了這個新對象),繼承函數的原型;

  • 執行構造函數中的代碼;

  • 返回新對象。

看完上面的內容,大家想必也知道this在對象的構造函數內的指向了吧!當你使用new關鍵字創建一個對象的新的實例時, this關鍵字指向這個實例 .

舉個栗子:

function Human (age) {
    this.age = age;
}
let greg = new Human(22);
let thomas = new Human(24);

console.log(greg); // this.age = 22
console.log(thomas); // this.age = 24

// answer
Person { age:22}
Person { age:24}

2.3 this在對象方法內

方法是與對象關聯的函數的通俗叫法, 如下所示:

let o = {
    sayThis(){
        console.log(this);
    }
}

如上所示,在對象的任何方法內的this都是指向對象本身 .

好了,繼續下一個環境!

2.4 this在簡單函數內

可能看到這裏,許多同學心裏會有疑問,什麼是簡單函數?

其實簡單函數大家都很熟悉,就像下面一樣,以相同形式編寫的匿名函數也被認為是簡單函數(非箭頭函數)。

function hello(){
    console.log("hello"+this);
}

這裏需要注意,在瀏覽器中,不管函數聲明在哪裡,匿名或者不匿名,只要不是直接作為對象的方法,this指向始終是window對象(除非使用call,apply,bind修改this指向)。

舉個栗子說明一下:

// 显示函數,直接定義在sayThis方法內,this指向依舊不變
function simpleFunction() {
    console.log(this);
}

var o = {
    sayThis() {
        simpleFunction();
    }
}

simpleFunction(); // Window
o.sayThis(); // Window


// 匿名函數
var o = {
    sayThis(){
        (function(){consoloe.log(this);})();
    }
} 
o.sayThis();// Window

對於初學者來說,this在簡單函數內的表現時常讓他們懵逼不已,難道this不應該指向對象本身?這個問題曾經也出現在我的腦海里過,沒錯,在寫代碼時我也踩過這個坑。

通常的,當我們要在對象方法內調用函數,而這個函數需要用到this時,我們都會創建一個變量來保存對象中的this的引用. 通常, 這個變量名稱叫做self或者that。具體說下所示:

const o = {
    doSomethingLater() {
        const self = this;
        setTimeout(function() {
            self.speakLeet();
        }, 1000);
    },
    speakLeet() {
        console.log(`1337 15 4W350M3`);
    }
}

o.doSomethingLater(); // `1337 15 4W350M3`

心細的同學可能已經發現,這裏的簡單函數沒有將箭頭函數包括在內,那麼下一個環境是什麼想必也能猜到啦,那麼現在進入下一個環境,看看this指向什麼。

2.5 this在箭頭函數內

和簡單函數表現不太一樣,this在箭頭函數中總是跟它在箭頭函數所在作用域的this一樣(在它直接作用域). 所以, 如果你在對象中使用箭頭函數, 箭頭函數中的this總是指向這個對象本身, 而不是指向Window.

下面我們使用箭頭函數,重寫一下上面的案例:

const o = {
    doSomethingLater() {
        setTimeout(() => this.speakLeet(), 1000);
    },
    speakLeet() {
        console.log(`1337 15 4W350M3`);
    }
}
o.doSomethingLater(); // `1337 15 4W350M3`

最後,讓我們來看看最後一種環境 – 事件偵聽器.

2.6 this在事件偵聽器內

在事件偵聽器內, this被綁定的是觸發這個事件的元素:

let button = document.querySelector('button');

button.addEventListener('click', function() {
    console.log(this); // button
});

3. this綁定規則

事實上,只要記住上面this在不同環境的綁定值,足以應付大部分工作。然而,好學的同學總是會忍不住想說,為什麼呢?對,為什麼this在這些情況下綁定這些值呢?學習,我們不能只知其然,而不知所以然。所以,現在就讓我們來探尋,this值獲取的真相吧。

現在,讓我們回憶一下,在講什麼是this的時候,我們說到“this的綁定取決於他所處的環境”。這句話其實不是十分準確,準確的說,this不是編寫時綁定,而是運行時綁定。它依賴於函數調用的上下文條件this綁定和函數聲明的位置無關,反而和函數被調用的方式有關

當一個函數被調用時,會建立一個活動記錄,也稱為執行環境。這個記錄包含函數是從何處(call-stack)被調用的,函數是 如何被調用的,被傳遞了什麼參數等信息。這個記錄的屬性之一,就是在函數執行期間將被使用的this引用。this實際上是在函數被調用時建立的一個綁定,它指向什麼是完全由函數被調用的調用點來決定的

僅僅是規則

現在我們將注意力轉移到調用點 如何 決定在函數執行期間this指向哪裡。

你必須考察call-site並判定4種規則中的哪一個適用。我們將首先獨立的解釋一下這4種規則中的每一種,之後我們來展示一下如果有多種規則可以適用調用點時,它們的優先級。

3.1 默認綁定規則

第一種規則來源於函數調用的最常見的情況:獨立函數調用。可以認為這種this規則是在沒有其他規則適用時的默認規則。我們給它一個稱呼“默認綁定”.

現在來看這段代碼:

function foo(){
    console.log(this); 
}
var a = 2;
demo(); // 2

當foo()被調用時,this.a解析為我們的全局變量a。為什麼?因為在這種情況下,對此方法調用的this實施了 默認綁定,所以使this指向了全局對象。

在我們的代碼段中,foo()是被一個直白的,毫無修飾的函數引用調用的。沒有其他的我們將要展示的規則適用於這裏,所以 默認綁定 在這裏適用。

如果strict mode在這裏生效,那麼對於 默認綁定 來說全局對象是不合法的,所以this將被設置為undefined。

'use strict'
function foo(){
    console.log(this.a); // TypeError: Cannot read property 'a' of undefined
}
const a = 1;
foo();
function foo(){
	'use strict'
    console.log(this.a); // TypeError: Cannot read property 'a' of undefined
}
const a = 1;
foo();

微妙的是,即便所有的this綁定規則都是完全基於調用點,如果foo()的 內容 沒有在strint mode下執行,對於 默認綁定 來說全局對象是 唯一 合法的;foo()的call-site的strict mode狀態與此無關。

function foo(){
    console.log(this.a); 
}
var a = 1;
(function(){
	'use strict';
	foo(); // 1
})();

注意: 在代碼中故意混用strict mode和非strict mode通常是讓人皺眉頭的。你的程序整體可能應當不是 Strict 就是非Strict。然而,有時你可能會引用與你的 Strict 模式不同的第三方包,所以對這些微妙的兼容性細節要多加小心。

3.2 隱式綁定

另一種要考慮的規則是:調用點是否有一個環境對象(context object),也稱為擁有者(owning)或容器(containing)對象。

讓我們來看這段代碼:

function foo() {
    console.log(this.a);
}
let o = {
    a: 2,
    foo,
}
o.foo(); // 2

這裏,我們注意到foo函數被聲明然後作為對象o的方法,無論foo()是否一開始就在obj上被聲明,還是後來作為引用添加(如上面代碼所示),都是這個 函數 被obj所“擁有”或“包含”。這裏,調用點使用obj環境來引用函數,所以可以說 obj對象在函數被調用的時間點上“擁有”或“包含”這個 函數引用。

當一個方法引用存在一個環境對象時,隱式綁定 規則會說:是這個對象應當被用於這個函數調用的this綁定。

只有對象屬性引用鏈的最後一層是影響調用點的。比如:

function foo(){
    console.log(this.a);
}

var obj1 = {
    a:2,
    obj2:obj2
};
var obj2 = {
    a:42,
    foo:foo
};
obj1.obj2.foo(); // 42

隱式綁定的隱患

當一個 隱含綁定丟失了它的綁定,這通常意味着它會退回到 默認綁定, 根據strict mode的狀態,結果不是全局對象就是undefined。

下面來看這段代碼:

function foo(){
    console.log(this.a);
}

var obj = {
    a:2,
    foo
};
var bar = obj.foo;
var a = "Global variable";
bar(); // "Global variable"

儘管bar似乎是obj.foo的引用,但實際上它只是另一個foo自己的引用而已。另外,起作用的調用點是bar(),一個直白,毫無修飾的調用,因此 默認綁定 適用於這裏。

這種情況發生的更加微妙,更常見,更意外的方式,是當我們考慮傳遞一個回調函數時:

function foo(){
    console.log(this.a);
}

function doFoo(fn){
	fn();
}

var obj = {
    a:2,
    foo,
};
var a = "Global variable";
dooFoo(obj.foo); // "Global variable"

參數傳遞僅僅是一種隱含的賦值,而且因為我們在傳遞一個函數,它是一個隱含的引用賦值,所以最終結果和我們前一個代碼段一樣。同樣的,語言內建,如setTimeout也一樣,如下所示

function foo(){
    console.log(this.a);
}

var obj = {
    a:2,
    foo,
};
var a = "Global variable";
setTimeout(obj.foo, 100); // "Global variable"

把這個粗糙的setTimeout()假想實現當做JavaScript環境內建的實現的話:

function setTimeout(fn, delay){
    // 等待delay毫秒
    fn();
}

正如我們看到的, 隱含綁定丟失了它的綁定是十分常見的,不管哪一種意外改變this的方式,你都不能真正地控制你的回調函數引用將如何被執行,所以你(還)沒有辦法控制調用點給你一個故意的綁定。但是我們可以使用显示綁定強行固定this。

3.3 显示綁定

我們看到隱含綁定,需要我們不得不改變目標對象使它自身包含一個對函數的引用,而後使用這個函數引用屬性來間接地(隱含地)將this綁定到這個對象上。

但是,如果你想強制一個函數調用使用某個特定對象作為this綁定,而不在這個對象上放置一個函數引用屬性呢?

js有提供call()、apply()方法,ES5中也提供了內置的方法 Function.prototype.bind,可以引用一個對象時進行強制綁定調用。

考慮這段代碼:

function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
};
foo.call(obj); // 2

通過foo.call(..)使用 明確綁定 來調用foo,允許我們強制函數的this指向obj。

如果你傳遞一個簡單原始類型值(string,boolean,或 number類型)作為this綁定,那麼這個原始類型值會被包裝在它的對象類型中(分別是new String(..),new Boolean(..),或new Number(..))。這通常稱為“boxing(封箱)”。

注意: 就this綁定的角度講,call(..)和apply(..)是完全一樣的。它們確實在處理其他參數上的方式不同,但那不是我們當前關心的。

單獨依靠call和apply,仍然可能出現函數“丟失”自己原本的this綁定,或者被第三方覆蓋等問題。

但有一個技巧可以避免出現這些問題

考慮這段代碼:

function foo(){
    console.log(this.a);
}
var obj = {
	a:2
};
var bar = function(){
	foo.call(obj);
}
bar(); // 2
setTimeout(bar, 100); // 2
bar.call(window); // 2

我們創建了一個函數bar(),在它的內部手動調用foo.call(obj),由此強制this綁定到obj並調用foo。無論你過後怎樣調用函數bar,它總是手動使用obj調用foo。這種綁定即明確又堅定,該方法被開發者稱為 硬綁定(显示綁定的變種)(hard binding)

用硬綁定將一個函數包裝起來的最典型的方法,是為所有傳入的參數和傳出的返回值創建一個通道:

function foo(something){
    console.log(this.a, something);
    return this.a + something;
}
var obj = {
    a:2
};
var bar = function() {
    return foo.apply(obj, arguments);
}
var b = bar(3);
console.log(b); //  5

另一種表達這種模式的方法是創建一個可復用的幫助函數:

function foo(something){
    console.log(this.a, something);
    return this.a + something;
}

function bind(fn, obj){
    return function(){
        return fn.apply(obj, arguments);
    };
}

var obj = { a:2};
var bar = bind(foo, obj);
var b = bar(3);
console.log(b); // 5

由於 硬綁定 是一個如此常用的模式,它已作為ES5的內建工具提供,即前文提到的Function.prototype.bind:

function foo(something){
    console.log(this.a, something);
    return this.a + something;
}
var obj = { a:2};
var bar = foo.bind(obj);
var b = bar();
cobsole.log(b); // 5

bind(..)返回一個硬編碼的新函數,它使用你指定的this環境來調用原本的函數。

注意: 在ES6中,bind(..)生成的硬綁定函數有一個名為.name的屬性,它源自於原始的 目標函數(target function)。舉例來說:bar = foo.bind(..)應該會有一個bar.name屬性,它的值為”bound foo”,這個值應當會显示在調用棧軌跡的函數調用名稱中。

3.4new 綁定

第四種也是最後一種this綁定規則

當在函數前面被加入new調用時,也就是構造器調用時,下面這些事情會自動完成:

  • 一個全新的對象會憑空創建(就是被構建)
  • 這個新構建的對象會被接入原形鏈([[Prototype]]-linked)
  • 這個新構建的對象被設置為函數調用的this綁定
  • 除非函數返回一個它自己的其他 對象,這個被new調用的函數將 自動 返回這個新構建的對象。

考慮這段代碼:

function foo(a){
    console.log(this.a);
}
var bar = new foo(2);
console.log(bar.a); // 2

通過在前面使用new來調用foo(..),我們構建了一個新的對象並這個新對象作為foo(..)調用的this。 new是函數調用可以綁定this的最後一種方式,我們稱之為 new綁定(new binding)。

3.5 優先級

  • new綁定
  • 显示綁定
  • 隱式綁定
  • 默認綁定(嚴格模式下會綁定到undefined)

4. 箭頭函數

箭頭函數並非使用function關鍵字進行定義,而是通過所謂的“大箭頭”操作符:=>,所以不會使用上面所講解的this四種標準規範,箭頭函數從封閉它的(function或global)作用域採用this綁定,即箭頭函數會繼承自外層函數調用的this綁定。

執行 fruit.call(apple)時,箭頭函數this已被綁定,無法再次被修改。

function fruit(){
    return () => {
        console.log(this.name);
    }
}
var apple = {
    name: '蘋果'
}
var banana = {
    name: '香蕉'
}
var fruitCall = fruit.call(apple);
fruitCall.call(banana); // 蘋果

5. 小結

this是JavaScript的一個關鍵字,this不是編寫時綁定,而是運行時綁定。它依賴於函數調用的上下文條件。this綁定和函數聲明的位置無關,反而和函數被調用的方式有關。為執行中的函數判定this綁定需要找到這個函數的直接調用點。找到之後,4種規則將會以 這個 優先順序施用於調用點:

  • 被new調用?使用新構建的對象。
  • 被call或apply(或 bind)調用?使用指定的對象。
  • 被持有調用的環境對象調用?使用那個環境對象。
  • 默認:strict mode下是undefined,否則就是全局對

與這4種綁定規則不同,ES6的箭頭方法使用詞法作用域來決定this綁定,這意味着它們採用封閉他們的函數調用作為this綁定(無論它是什麼)。它們實質上是ES6之前的self = this代碼的語法替代品。

參考文章:

深入理解JavScript中的this

詳解JavaScript中的this

你不懂this:豁然開朗

你不懂this:this是什麼?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

MOJITO 發布一周,爬一波彈幕分析下

MOJITO

最近一直啥都沒寫,追個熱點都趕不上熱乎的,鄙視自己一下。

周董的新歌 「MOJITO」 發售(6 月 12 日的零點)至今大致過去了一周,翻開 B 站 MV 一看,播放量妥妥破千萬,彈幕破十萬,這人氣還真是杠杠的。

說實話, 「MOJITO」 這個名字對我來講有點超綱了,第一次見到完全不知道啥意思。

不過問題不大,沒有什麼是百度解決不了的,如果有,那就再加一個知乎。

MOJITO 的中文名是莫吉托,百度百科上是這麼介紹莫吉托的:

莫吉托(Mojito)是最有名的朗姆調酒之一。起源於古巴。傳統上,莫吉托是一種由五種材料製成的雞尾酒:淡朗姆酒、糖(傳統上是用甘蔗汁)、萊姆(青檸)汁、蘇打水和薄荷。最原始的古巴配方是使用留蘭香或古巴島上常見的檸檬薄荷。萊姆(青檸)與薄荷的清爽口味是為了與朗姆酒的烈性相互補,同時也使得這種透明無色的調酒成為夏日的熱門飲料之一。這種調酒有着相對低的酒精含量(大約10%)。

酒精度數在 10% 左右的話,姑且可以認為一種飲料吧。

當然,如果要開車的話就不能把 MOJITO 當成飲料了,酒精含量再低那也是酒精。

整個 MV 我翻來覆去的看了好幾遍, 「MOJITO」 這個東西除了在歌詞和名字中有出現,在 MV 當中一次都沒出現,毫無存在感。

爬取 B 站彈幕

彈幕數據的爬取比較簡單,我就不一步一步的抓請求給各位演示了,注意下面這幾個請求連接:

彈幕請求地址:

https://api.bilibili.com/x/v1/dm/list.so?oid=XXX

https://comment.bilibili.com/XXX.xml

第一個地址由於 B 站的網頁做了更換,現在在 Chrome 工具的 network 裏面已經找不到了,不過還可以用,這個是我之前找到的。

第二個地址來源於百度,我也不知道各路大神是從哪找出來這個地址的,供參考吧。

上面這兩個彈幕地址實際上都需要一個叫 oid 的東西,這個 oid 獲取方式如下:

首先可以找到一個目錄頁接口:

https://api.bilibili.com/x/player/pagelist?bvid=XXX&jsonp=jsonp

這個接口也是來源於 Chrome 的 network ,其中 bvid 這個參數來源於視頻地址,比如周董的這個 「MOJITO」 的 MV ,地址是 https://www.bilibili.com/video/BV1PK4y1b7dt ,那麼這個 bvid 的值就是最後那一部分 BV1PK4y1b7dt

接下來在 https://api.bilibili.com/x/player/pagelist?bvid=BV1PK4y1b7dt&jsonp=jsonp 這個接口中,我們可以看到返回的 json 參數,如下:

{
    "code":0,
    "message":"0",
    "ttl":1,
    "data":[
        {
            "cid":201056987,
            "page":1,
            "from":"vupload",
            "part":"JAY-MOJITO_完整MV(更新版)",
            "duration":189,
            "vid":"",
            "weblink":"",
            "dimension":{
                "width":1920,
                "height":1080,
                "rotate":0
            }
        }
    ]
}

注意:由於這個 MV 只有一個完整的視頻,所以這裏只有一個 cid ,如果一個視頻是分不同小節發布的,這裏就會有多個 cid ,不同的 cid 代表不同的視頻。

當然,這裏的 cid 就是我們剛才想找的那個 oid ,把這個 cid 拼到剛才的鏈接上,可以得到 https://api.bilibili.com/x/v1/dm/list.so?oid=201056987 這樣一個地址,然後輸入到瀏覽器中,可以看到彈幕的返回數據,是一個 xml 格式的文本。

源代碼如下:

import requests
import re

# 獲取 cid
res = requests.get("https://api.bilibili.com/x/player/pagelist?bvid=BV1PK4y1b7dt&jsonp=jsonp")
cid = res.json()['data'][0]['cid']

# 將彈幕 xml 通過正則取出,生成 list
danmu_url = f"https://api.bilibili.com/x/v1/dm/list.so?oid={cid}"
result = requests.get(danmu_url).content.decode('utf-8')
pattern = re.compile('<d.*?>(.*?)</d>')
danmu_list = pattern.findall(result)

# 將彈幕 list 保存至 txt 文件
with open("dan_mu.txt", mode="w", encoding="utf-8") as f:
    for item in danmu_list:
        f.write(item)
        f.write("\n")

這裏我將獲取到的彈幕保存在了 dan_mu.txt 文件中,方便後續分析。

繪製詞雲圖

第一步先將剛才保存在 dan_mu.txt 文件中的彈幕讀取出來,放到了一個 list 當中:

# 讀取彈幕 txt 文件
with open("dan_mu.txt", encoding="utf-8") as f:
    txt = f.read()
danmu_list = txt.split("\n")

然後使用分詞工具對彈幕進行分詞,我這裏使用的分詞工具是最好的 Python 中文分詞組件 jieba ,沒有安裝過 jieba 的同學可以使用以下命令進行安裝:

pip install jieba

使用 jieba 對剛才獲得的彈幕 list 進行分詞:

# jieba 分詞
danmu_cut = [jieba.lcut(item) for item in danmu_list]

這樣,我們獲得了分詞后的 danmu_cut ,這個同樣是一個 list 。

接着我們對分詞后的 danmu_cut 進行下一項操作,去除停用詞:

# 獲取停用詞
with open("baidu_stopwords.txt",encoding="utf-8") as f:
    stop = f.read()
stop_words = stop.split()

# 去掉停用詞后的最終詞
s_data_cut = pd.Series(danmu_cut)
all_words_after = s_data_cut.apply(lambda x:[i for i in x if i not in stop])

這裏我引入了一個 baidu_stopwords.txt 文件,這個文件是百度停用詞庫,這裏我找到了幾個常用的中文停用詞庫,來源: https://github.com/goto456/stopwords 。

詞表文件 詞表名
baidu_stopwords.txt 百度停用詞表
hit_stopwords.txt 哈工大停用詞表
scu_stopwords.txt 四川大學機器智能實驗室停用詞庫
cn_stopwords.txt 中文停用詞表

這裏我使用的是百度停用詞表,大家可以根據自己的需要使用,也可以對這幾個停用詞表先做整合后再使用,主要的目的就是去除一些無需關注的詞,上面這幾個停用詞庫我都會提交到代碼倉庫,有需要的自取。

接着我們統計去除停用詞后的詞頻:

# 詞頻統計
all_words = []
for i in all_words_after:
    all_words.extend(i)
word_count = pd.Series(all_words).value_counts()

最後一步就是生成我們的最終結果,詞雲圖:

wordcloud.WordCloud(
    font_path='msyh.ttc',
    background_color="#fff",
    max_words=1000,
    max_font_size=200,
    random_state=42,
    width=900,
    height=1600
).fit_words(word_count).to_file("wordcloud.png")

最終結果就是下面這個:

從上面這個詞雲圖中可以看到,粉絲對「MOJITO」這首歌是真愛啊,出現頻率最高的就是 啊啊啊 還有

當然哈,這個 也有可能是說 MV 當中那台騷氣十足的粉色的老爺車。

還有一個出現頻率比較高的是 爺青回 ,我估計這個意思應該是 爺的青春回來啦 ,確實,周董伴隨着我這個年齡段的人一路走來,做為一位 79 年的人現在已經是 41 歲的「高齡」了,回首往昔,讓人唏噓不已。

當年一首 「雙節棍」 火遍了中華大地,大街上的音像店整天都在循環這幾首歌,在學校上學的我這一代人,基本上是人人都能哼兩句,「快使用雙截棍,哼哼哈嘿」成了我們這一代人共有的回憶。

智能情感傾向分析

我們還可以對彈幕進行一次情感傾向分析,這裏我使用的是 「百度 AI 開放平台」 的情感傾向分析接口。

百度 AI 開放平台文檔地址:https://ai.baidu.com/ai-doc/NLP/zk6z52hds

首先是根據文檔接入 「百度 AI 開放平台」 ,獲取 access_token ,代碼如下:

# 獲取 Baidu API access_token
access_token_url = f'https://aip.baidubce.com/oauth/2.0/token?grant_type={grant_type}&client_id={client_id}&client_secret={client_secret}&'

res = requests.post(access_token_url)

access_token = res.json()['access_token']

# 通用情感接口
# sentiment_url = f'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify?charset=UTF-8&access_token={access_token}'
# 定製化情感接口
sentiment_url = f'https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify_custom?charset=UTF-8&access_token={access_token}'

百度 AI 開放平台有兩個情感分析接口,一個是通用的,還有一個是定製化的,我這裏使用的是經過訓練的定製化的接口,如果沒有定製化的接口,使用通用的接口也沒有問題。

上面使用到的 grant_typeclient_idclient_secret 這幾個參數,大家註冊一下就能得到, 「百度 AI 開放平台」 上的這些接口都有調用數量的限制,不過我們自己使用已經足夠了。

然後讀取我們剛才保存的彈幕文本:

with open("dan_mu.txt", encoding="utf-8") as f:
    txt = f.read()
danmu_cat = txt.split("\n")

在調用接口獲得情感傾向之前,我們還需要做一件事情,對彈幕進行一次處理,因為彈幕中會有一些 emoji 表情,而 emoji 直接請求百度的接口會返回錯誤,這裏我使用另一個工具包對 emoji 表情進行處理。

首先安裝工具包 emoji :

pip install emoji

使用是非常簡單的,我們對彈幕數據使用 emoji 進行一次處理:

import emoji

with open("dan_mu.txt", encoding="utf-8") as f:
    txt = f.read()
danmu_list = txt.split("\n")

for item in danmu_list:
    print(emoji.demojize(item))

我們的彈幕數據中是有這樣的 emoji 表情的:



# 處理后:
:red_heart::red_heart::red_heart::red_heart::red_heart::red_heart::red_heart:

然後,我們就可以調用百度的情感傾向分析接口,對我們的彈幕數據進行分析了:

# 情感計數器
optimistic = 0
neutral = 0
pessimistic = 0

for danmu in danmu_list:
    # 因調用 QPS 限制,每次調用間隔 0.5s
    time.sleep(0.5)
    req_data = {
        'text': emoji.demojize(danmu)
    }
    # 調用情感傾向分析接口
    if len(danmu) > 0:
        r = requests.post(sentiment_url, json = req_data)
        print(r.json())
        for item in r.json()['items']:
            if item['sentiment'] == 2:
                # 正向情感
                optimistic += 1
            if item['sentiment'] == 1:
                # 中性情感
                neutral += 1
            if item['sentiment'] == 0:
                # 負向情感
                pessimistic += 1

print('正向情感:', optimistic)
print('中性情感:', neutral)
print('負向情感:', pessimistic)

attr = ['正向情感','中性情感','負向情感']
value = [optimistic, neutral, pessimistic]

c = (
    Pie()
    .add("", [list(attr) for attr in zip(attr, value)])
    .set_global_opts(title_opts=opts.TitleOpts(title="「MOJITO」彈幕情感分析"))
    .render("pie_base.html")
)

最後的結果圖長這樣:

從最後的結果上來看,正向情感佔比大約在 2/3 左右,而負向情感只有不到 1/4 ,看來大多數人看到周董的新歌還是滿懷激動的心情。

不過這個數據不一定準確,最多可以做一個參考。

源代碼

需要源代碼的同學可以在公眾號後台回復「MOJITO」獲取。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧